관리 메뉴

나만을 위한 블로그

[Android] TedImagePicker 사용법 본문

Android

[Android] TedImagePicker 사용법

참깨빵위에참깨빵 2023. 12. 22. 23:26
728x90
반응형

핸드폰 안의 사진들을 바텀 시트 형태로 가져오고 썸네일로 표시해주는 라이브러리인 TedBottomPicker가 4개월 전에 deprecated되었다.

 

https://github.com/ParkSangGwon/TedBottomPicker

 

GitHub - ParkSangGwon/TedBottomPicker: TedBottomPicker is simple image picker using bottom sheet

TedBottomPicker is simple image picker using bottom sheet - GitHub - ParkSangGwon/TedBottomPicker: TedBottomPicker is simple image picker using bottom sheet

github.com

 

즉 TedBottomPicker가 언제 지원 종료되거나 사라질지 모르는 상태가 되었다는 것이다.

그럼 이제 다른 라이브러리를 발굴하거나 커스텀 뷰를 써서 만들어야 하는가? 그건 아니다. 위 사진을 보다시피 제작자가 안드로이드 13 이상에서도 사용할 수 있는 라이브러리인 TedImagePicker를 만들었다.

 

https://github.com/ParkSangGwon/TedImagePicker

 

GitHub - ParkSangGwon/TedImagePicker: TedImagePicker is simple/beautiful/smart image picker

TedImagePicker is simple/beautiful/smart image picker - GitHub - ParkSangGwon/TedImagePicker: TedImagePicker is simple/beautiful/smart image picker

github.com

 

예전엔 몰라도 지금은 조금 올드한 느낌인 UI를 가진 전작에 비해 UI가 좀 더 깔끔해졌다. 또한 전작과 마찬가지로 기본 폴더(카메라, 스크린샷, 다운로드, favorites) 이외의 다른 폴더에 저장된 사진들도 가져올 수 있다.

내부적으로 TedPermission을 사용하고 있어서 라이브러리 사용을 위해 파일 권한을 요청하는 코드를 작성할 필요가 없다. 필요한 것은 카메라 권한이 요청되었는지 확인하는 코드와 라이브러리 함수를 조합한 코드, 가져온 이미지를 처리하는 코드뿐이다.

그럼 이 TedImagePicker는 어떻게 사용하는지 확인한다. 먼저 의존성부터 app gradle에 넣고 본다.

 

implementation 'io.github.ParkSangGwon:tedimagepicker:1.4.2'

 

이후 앱을 실행했을 때 앱이 죽는다면, 아마 TedPermission 또는 TedBottomPicker 라이브러리 중 하나, 아니면 둘 다 프로젝트에 포함돼 있을 거라고 생각된다. 이 경우 두 라이브러리 모두 제거하고 재실행하면 앱이 죽지 않고 정상적으로 실행될 것이다.

 

매니페스트에 카메라 권한도 추가해 준다. 라이브러리 UI에 카메라 버튼이 있지만 권한이 없으면 카메라 버튼을 누른 뒤 잠시 후 앱이 죽어버린다.

그래서 카메라 권한을 요청하고 적용하는 로직은 내가 직접 만들어야 한다. 아래의 액티비티 코드를 참고하면 확인할 수 있다.

 

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
    android:name="android.hardware.camera"
    android:required="false" />

 

그리고 XML에 기능 테스트를 위한 버튼을 2개 만든다. 각각 이미지 하나만 가져오기, 여러 이미지를 가져오기 위해 사용할 버튼이다.

 

<?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"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/btnSingleImage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="단일 이미지 선택"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/btnMultipleImage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="여러 이미지 선택"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btnSingleImage" />

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

 

액티비티 코드는 대충 아래와 같이 쓴다.

 

import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.example.kotlinpractice.databinding.ActivityMainBinding
import gun0912.tedimagepicker.builder.TedImagePicker

class MainActivity : AppCompatActivity() {

    companion object {
        private val TAG = this::class.java.simpleName
        private const val SINGLE_IMAGE_CAMERA_PERMISSION_REQUEST_CODE = 100
        private const val MULTIPLE_IMAGE_CAMERA_PERMISSION_REQUEST_CODE = 101
    }

    private val binding: ActivityMainBinding by lazy {
        DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    private val permissionList = arrayOf(
        Manifest.permission.CAMERA
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding.run {
            lifecycleOwner = this@MainActivity

            btnSingleImage.setOnClickListener {
                if (isPermissionGranted(permissionList)) {
                    selectSingleImage()
                } else {
                    ActivityCompat.requestPermissions(
                        this@MainActivity,
                        permissionList,
                        SINGLE_IMAGE_CAMERA_PERMISSION_REQUEST_CODE
                    )
                }
            }

            btnMultipleImage.setOnClickListener {
                if (isPermissionGranted(permissionList)) {
                    selectMultipleImage()
                } else {
                    ActivityCompat.requestPermissions(
                        this@MainActivity,
                        permissionList,
                        MULTIPLE_IMAGE_CAMERA_PERMISSION_REQUEST_CODE
                    )
                }
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            SINGLE_IMAGE_CAMERA_PERMISSION_REQUEST_CODE -> {
                Log.e(TAG, "## 단일 이미지 - 카메라 권한 허용됨")
                selectSingleImage()
            }
            MULTIPLE_IMAGE_CAMERA_PERMISSION_REQUEST_CODE -> {
                Log.e(TAG, "## 여러 이미지 - 카메라 권한 허용됨")
                selectMultipleImage()
            }
        }
    }

    private fun selectSingleImage() {
        TedImagePicker.with(this)
            .start { uri ->
                Log.e(TAG, "## uri : $uri")
            }
    }

    private fun selectMultipleImage() =
        TedImagePicker.with(this)
            .max(5, "최대 5장의 사진만 선택 가능합니다")
            .startMultiImage { uris ->
                Log.e(TAG, "## 여러 사진 선택 : $uris")
                Log.e(TAG, "## uris size : ${uris.size}")
            }

    private fun isPermissionGranted(permission: Array<String>): Boolean = permission.all {
        ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
    }
}

 

 

위 코드가 어떻게 작동하는지는 아래 영상을 참고한다.

 

 

갤러리 또는 카메라에 접근해서 사진 파일을 얻은 뒤 메인 액티비티로 돌아오면 로그가 찍히고, 사진 여러 개를 가져올 경우 선택한 사진들의 개수가 로그로 찍히는 걸 볼 수 있다.

위 코드는 커스텀 없이 기본적인 함수만 조합해서 사용하는 예제다. 만약 완료 버튼에 표시되는 글자, 글자색, 버튼 위치와 버튼 모양을 바꾸려면 selectMultipleImage()를 아래와 같이 수정하고 드로어블 코드는 복붙한 다음 drawable 폴더에 넣는다. 단일 이미지 선택의 경우 완료 버튼이 표시되지 않기 때문에 아래의 버튼 관련 함수들을 추가하는 자체가 무의미하다.

 

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="10dp"
    android:shape="rectangle">
    <solid android:color="#FFF555" />
    <corners
        android:radius="20dp" />
</shape>
private fun selectMultipleImage() =
    TedImagePicker.with(this)
        .buttonText("완료 말고 다른 글자")
        .buttonTextColor(R.color.black)
        .buttonGravity(ButtonGravity.BOTTOM)
        .buttonBackground(R.drawable.bg_round_button)
        .max(5, "최대 5장의 사진만 선택 가능합니다")
        .startMultiImage { uris ->
            Log.e(TAG, "## 여러 사진 선택 : $uris")
            Log.e(TAG, "## uris size : ${uris.size}")
        }

 

그리고 사진을 선택하면 버튼이 내가 넣은 설정대로 우측 하단에 표시되는 걸 볼 수 있다.

 

 

이외에도 여러 커스텀 가능한 요소들이 있으니 본인 입맛에 맞게 커스텀 가능한 부분은 커스텀해서 쓰면 되겠다.

반응형
Comments