본문 바로가기
Android

카메라 앨범으로부터 사진 가져오기

by kldaji 2021. 7. 6.

1. Permission 추가

// AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

 

2. xml

- TouchIamgeView에 대한 설명은 (https://kldaji.tistory.com/3) 해당 링크를 참조하자.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     
     	<Button
            android:id="@+id/get_image_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
            
        <com.ortiz.touchview.TouchImageView
            android:id="@+id/touch_image"
            android:layout_width="400dp"
            android:layout_height="400dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"        
            app:layout_constraintTop_toBottomOf="@id/get_image_button"/>
      
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

3. fragment에 함수 추가

- $표시 붙은 부분은 사용자가 사용하는 이름으로 변경해주면 된다.

class $FragmentName : Fragment() {
    // const
    private val REQUEST_CODE = 100

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
    
        // binding
        val binding: $FragmentNameBinding = DataBindingUtil.inflate(
                inflater, R.layout.$fragment_name, container, false
        )

        // Get Image Button Click
        binding.getImageButton.setOnClickListener {
            // Pick Picture From Gallery
            openGalleryForImage()
        }

        return binding.root
    }

    // Pick Picture From Gallery
    private fun openGalleryForImage() {
        val intent = Intent(Intent.ACTION_PICK)
        intent.type = "image/*"
        startActivityForResult(intent, REQUEST_CODE)
    }

    // Pick Picture From Gallery
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE){
            // Set ImageView
            touch_image.setImageURI(data?.data)
        }
    }
}

 

4. 갤러리에서 가져온 사진을 터치하면 새로운 Activity에 full screen 이미지로 나오게 하기.

<번외편>

(https://developer.android.com/training/basics/firstapp/starting-activity) 해당 문서를 참고하였다.

 

1) Activity 추가

class $ActivityName : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = $ActivityNameBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.ortiz.touchview.TouchImageView
            android:id="@+id/touch_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

2) Intent 추가

class $FragmentName : Fragment() {
    // const
    private val REQUEST_CODE = 100
    
    // image uri
    private var uri: Uri? = null

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
    
        // binding
        val binding: $FragmentNameBinding = DataBindingUtil.inflate(
                inflater, R.layout.$fragment_name, container, false
        )

        // Get Image Button Click
        binding.getImageButton.setOnClickListener {
            // Pick Picture From Gallery
            openGalleryForImage()
        }
        
        // Image Click
        binding.iv.setOnClickListener(View.OnClickListener {
            sendImage(it)
        })
        
        // touch image
        if(uri != null){
            binding.touchImage.setImageURI(uri)
        }

        return binding.root
    }

    // Pick Picture From Gallery
    private fun openGalleryForImage() {
        val intent = Intent(Intent.ACTION_PICK)
        intent.type = "image/*"
        startActivityForResult(intent, REQUEST_CODE)
    }

    // Pick Picture From Gallery
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE){
            // Set Uri   
            uri = data?.data!!
            
            // Set ImageView
            touch_image.setImageURI(uri)
        }
    }

    // Send Image Uri
    private fun sendImage(view: View){
    	// Intent
        val intent = Intent(requireContext(), $ActivityName::class.java).apply {
            putExtra("ImageUri", uri.toString())
        }
        startActivity(intent)
    }
}

 

3) Activity 수정

class $ActivityName : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = $ActivityNameBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        // Get Uri
        val uri = intent.getStringExtra("ImageUri")
        binding.touchImage.setImageURI(uri.toUri())
    }
}

 

 

 

 

<21.07.08 추가>

※ 그런데 생각해보니까 액티비티를 새로 생성하는 것보다 fragment를 새로 만들어서 navigation을 통해 uri를 전달하도록 하는 것이 더 간단할 것이라는 생각이 들어 새로 구현을 해보았다.

 

1) fragment 추가

// $newFragmentName
class $newFragmentName : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // binding
        val binding: ${newFragmentName}Binding = DataBindingUtil.inflate(
            inflater, R.layout.${newFragmentName}, container, false
        )

        // argument
        val arg = ${newFragmentName}Args.fromBundle(requireArguments())

        // 이미지 설정
        binding.touchImage.setImageURI(arg.uri.toUri())

        return binding.root
    }
}

 

2) 추가한 fragment layout

// $newFragmentName.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.ortiz.touchview.TouchImageView
            android:id="@+id/touch_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

3) navigation 추가

<?xml version="1.0" encoding="utf-8"?>
<navigation
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/navigation"
    app:startDestination="@id/$fragmentName">

    <fragment
        android:id="@+id/$fragmentName"
        android:name="${package}.${fragmentName}"
        android:label="${fragmentName}"
        tools:layout="@layout/${layoutName}" >
        <action
            android:id="@+id/action_${fragmentName}_to_${newFragmentName}"
            app:destination="@id/${newFragmentName}" />
    </fragment>
   
    <fragment
        android:id="@+id/${newFragmentName}"
        android:name="${package}.${newFragmentName}"
        android:label="${newFragmentName}" >
        <argument
            android:name="uri"
            app:argType="string" />
    </fragment>
</navigation>

 

4) fragment viewModel Factory

// ${fragmentName}ViewModelFactory
class ${fragmentName}ViewModelFactory(): ViewModelProvider.Factory {
    @Suppress("unchecked_cast")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if(modelClass.isAssignableFrom(${fragmentName}ViewModel::class.java)){
            return ${fragmentName}ViewModel() as T
        }
        throw IllegalArgumentException("Unknown ViewModel Class")
    }
}

 

5) fragment viewModel

// ${fragmentName}ViewModel
class ${fragmentName}ViewModel() : ViewModel() {

    private val _navigateTo${newFragmentName} = MutableLiveData<Boolean>()
    val navigateTo${newFragmentName}: LiveData<Boolean>
        get() = _navigateTo${newFragmentName}

    fun doneNavigating() {
        _navigateTo${newFragmentName}.value = null
    }


    fun goTo${newFragmentName}(){
        _navigateTo${newFragmentName}.value = true
    }

}

 

6) fragment 수정

class $FragmentName : Fragment() {
    // const
    private val REQUEST_CODE = 100
    
    // uri
    private var uri: Uri? = null

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
    
        // binding
        val binding: $FragmentNameBinding = DataBindingUtil.inflate(
                inflater, R.layout.$fragment_name, container, false
        )
        
        val viewModelFactory = NewClientViewModelFactory(args.clientId, dao)

        val viewModel = ViewModelProvider(this, viewModelFactory).get(${fragmentName}ViewModel::class.java)

        binding.lifecycleOwner = this
        
        viewModel.navigateTo${newFragmentName}.observe(viewLifecycleOwner, Observer {
            if (it == true){
                this.findNavController().navigate(${fragmentName}Directions.action${fragmentName}To${newFragmentName}(uri.toString()))
                viewModel.doneNavigating()
            }
        })

        // Get Image Button Click
        binding.getImageButton.setOnClickListener {
            // Pick Picture From Gallery
            openGalleryForImage()
        }
        
        // Touch Image View Click
        binding.touchImage.setOnClickListener(View.OnClickListener {
            // set navigate flag true
            viewModel.goTo${newFragmentName}()
        })
        
        // touch image
        if(uri != null){
            binding.touchImage.setImageURI(uri)
        }

        return binding.root
    }

    // Pick Picture From Gallery
    private fun openGalleryForImage() {
        val intent = Intent(Intent.ACTION_PICK)
        intent.type = "image/*"
        startActivityForResult(intent, REQUEST_CODE)
    }

    // Pick Picture From Gallery
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE){
            // Gallery Request

            // get uri
            uri = data?.data!!

            // Set ImageView
            touch_image.setImageURI(uri)
        }
    }
}

 

※ fragment를 통해 이미지를 확대하는 것을 구현할 때, navigation과 viewModel에 대한 코드가 들어가서 길어진 감이 있지만 navigation과 viewModel은 앞으로도 계속 사용할 것이기에 익숙해지는 것을 추천한다.