와챠의 우당탕탕 코딩 일기장

[Android/Kotlin]사진 최대 선택 개수 제한하기/limit the number of selected photo 본문

코딩 일기장/Android(Kotlin)

[Android/Kotlin]사진 최대 선택 개수 제한하기/limit the number of selected photo

minWachya 2022. 2. 27. 16:30
반응형

아래 화면은 "동네 고영희" 앱 UI입니다^____^

 

결과 화면

 

구현한 기능

  • 갤러리에서 사진 선택(사진 선택 시 최대 선택 개수 지정)
  • 사진 재선택 시 이전에 선택한 사진 보이기
  • 갤러리에서 사진 선택 시 확대된 사진 보이기
  • 선택된 사진 개수 실시간으로 보이기

 

1. 라이브러리 gradle에 추가

사진 개수 제한을 위한 fishbun과

이미지 로드를 위한 gilde 라이브러리를 다운받아준다.

최신 버전 확인: https://github.com/sangcomz/FishBun

dependencies {
	implementation 'io.github.sangcomz:fishbun:1.0.0-beta01'   // limit photo count
    	implementation 'com.github.bumptech.glide:glide:4.12.0' // glide
}

 

2. Manifest에 권한 추가

갤러리에 들어가야하니까... 관련 권한이 필요하다.

<!--사용자 갤러리에서 사진 가져오기-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

그리고 아래 코드도 application 태그 밖에 써준다.

<!--사용자 갤러리에서 사진 가져오기-->
<queries>
    <intent>
        <action android:name="android.media.action.IMAGE_CAPTURE" />
    </intent>
</queries>

 

3. 코드 작성

3-1. fragment_cat_add3.xml: 사진이 보일 xml

빨간 동그라미 부분 코드만~

<!--사진 선택 레이아웃-->
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/layoutSelectPhoto"
    android:layout_width="72dp"
    android:layout_height="72dp"
    android:layout_marginTop="16dp"
    android:layout_marginStart="20dp"
    android:background="@drawable/layout_border_round"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textPic">
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginTop="15dp"
        android:src="@drawable/ic_launcher_foreground"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/tvSlash"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/spoqa_han_sans_neo_regular"
        android:text="@string/cat_add_picture_slash"
        android:textColor="@color/beige_E3DECF"
        android:textSize="10sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />
    <TextView
        android:id="@+id/tvSelectCount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/spoqa_han_sans_neo_regular"
        android:text="@string/cat_add_picture_select_count"
        android:textColor="@color/yellow_F1BC35"
        android:textSize="10sp"
        app:layout_constraintEnd_toStartOf="@+id/tvSlash"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/spoqa_han_sans_neo_regular"
        android:text="@string/cat_add_picture_max_count"
        android:textColor="@color/beige_E3DECF"
        android:textSize="10sp"
        app:layout_constraintStart_toEndOf="@+id/tvSlash"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rcPhoto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:orientation="horizontal"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    app:layout_constraintBottom_toBottomOf="@+id/layoutSelectPhoto"
    app:layout_constraintStart_toEndOf="@+id/layoutSelectPhoto"
    app:layout_constraintTop_toTopOf="@+id/layoutSelectPhoto" />

 

3-2. list_item_cat_add_photo.xml: 리사이클러뷰 아이템 xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="72dp"
    android:layout_height="72dp"
    app:cardCornerRadius="4dp"
    android:layout_marginEnd="4dp">

    <ImageView
        android:id="@+id/imgCatAddPhoto"
        android:layout_width="72dp"
        android:layout_height="72dp"
        />

    <ImageView
        android:id="@+id/imgCatAddClose"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:background="@drawable/btn_close_round"
        android:src="@drawable/ic_btn_close"
        android:layout_margin="8dp"
        android:layout_gravity="right"/>

</androidx.cardview.widget.CardView>

 

4. Adapter 생성

앱 UI를 보니 사진 선택된 갯수를 보여주고/삭제될 때도 그 갯수가 바로 반영되어야 할 거 같았다.

그래서 CatAddFragment3.kt(fragment_cat_add3.xml)의 뷰를 CatAddPhotoAdapter(list_item_cat_add_photo.xml)에서 수정해야하는 상황이 발생했다.

해결 방법으로는

1, CatAddFragment3.kt 안에 CatAddPhotoAdapter를 코드를 작성하는 방법도 있고

2, CatAddPhotoAdapter에 CatAddFragment3의 뷰바인딩을 넘겨주는 방법도 있었다.

나는 클래스 너무 길어지는 게 싫어서 2번을 택했다!

CatAddPhotoAdapter.kt

설명은 주석에 ㅎ.ㅎ

import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.example.dongnaegoyang.R
import com.example.dongnaegoyang.databinding.FragmentCatAdd3Binding

// 고양이 추가 페이지-3단계: 사진 어댑터
class CatAddPhotoAdapter(val binding: FragmentCatAdd3Binding) : RecyclerView.Adapter<CatAddPhotoAdapter.ViewHolder>() {
    var imgUris = ArrayList<Uri>()  // Uri 배열

    // 뷰홀더 생성
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CatAddPhotoAdapter.ViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.list_item_cat_add_photo, parent, false)

        return ViewHolder(itemView)
    }

    // position 번째 아이템 설정하기
    override fun onBindViewHolder(holder: CatAddPhotoAdapter.ViewHolder, position: Int) {
        val uri = imgUris[position]
        holder.setItem(uri)
    }

    // 아이템 갯수 리턴
    override fun getItemCount() = imgUris.size

    // 아이템 삭제
    fun removeData(position: Int) {
        // 아이템 삭제
        imgUris.removeAt(position)
        notifyItemRemoved(position)
        // 현재 선택된 사진 개수 변경
        binding.tvSelectCount.text = itemCount.toString()
    }

    // 사진 로드하기
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun setItem(url: Uri) {
            // 이미지 로드
            Glide.with(itemView)
                .load(url)
                .error(R.drawable.ic_launcher_background)                  // 오류 시 이미지
                .apply(RequestOptions().centerCrop())
                .into(itemView.findViewById(R.id.imgCatAddPhoto))

            // 삭제 버튼
            itemView.findViewById<ImageView>(R.id.imgCatAddClose).setOnClickListener {
                removeData(this.layoutPosition)
            }
        }
    }
}

 

5. 실행 코드 작성

CatAddFragment3.kt

private const val TAG = "mmmCatAddFragment3"

private var _binding: FragmentCatAdd3Binding? = null
private val binding get() = _binding!!

// 고양이 추가: 3단계 프레그먼드
class CatAddFragment3 : Fragment() {
    private lateinit var photoAdapter: CatAddPhotoAdapter

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        _binding = FragmentCatAdd3Binding.inflate(inflater, container, false)
        val view = binding.root

        // 사진 어댑터 설정
        photoAdapter = CatAddPhotoAdapter(binding)
        binding.rcPhoto.adapter = photoAdapter
        setSelectPhoto()    // 사진 선택 설정

        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    // 사진 선택 설정
    private fun setSelectPhoto() {
        // 사진 설정 부분
        val photoResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                val data = result.data
                if (data != null) {
                    // 이전 데이터 삭제
                    photoAdapter.imgUris.clear()
                    // 새 데이터 저장
                    val photoUriArr = data.getParcelableArrayListExtra<Uri>(INTENT_PATH)!!
                    for (uri in photoUriArr) photoAdapter.imgUris.add(uri)
                    photoAdapter.notifyDataSetChanged()
                }
                // 사진 갯수
                binding.tvSelectCount.text = photoAdapter.itemCount.toString()
            }
        }
        binding.layoutSelectPhoto.setOnClickListener {
            FishBun.with(this@CatAddFragment3)
                .setImageAdapter(GlideAdapter())
                .setIsUseDetailView(true)                   // 상세 사진 보기 true
                .setMaxCount(6)                             // 최대 사진 개수
                .setSelectedImages(photoAdapter.imgUris)    // 이전에 선택했던 사진 uri 배열
                .setActionBarColor(Color.parseColor("#473A22"), Color.parseColor("#5D4037"), false)
                .setActionBarTitleColor(Color.parseColor("#ffffff"))
                .startAlbumWithActivityResultCallback(photoResultLauncher)
        }
    }
}

다행히 라이브러리가 있었다~~ 다행~~ 생각보다 너무 편하게 기능 구현이 가능해서 놀랐다.

짱...!!!!!!

반응형
Comments