Android - RecyclerView & Checkbox 활용 - 스크롤 시, 체크박스가 해제되는 바인딩 문제 해결 (3) (Kotlin)

업데이트:

체크박스 바인딩 이슈

개요

일반적으로 체크박스를 리사이클러뷰나 리스트뷰에 구현하게 되면 한 가지 문제점에 부딛힙니다.

image


다음 그림처럼 리사이클러뷰와 리스트뷰는 모든 뷰 객체를 한번에 생성하는게 아니라 화면에 보이는 일부 뷰만을 계속 생성하거나 재활용됩니다.

이 때, 체크박스 경우는 체크를 해제하거나 체크하였더라도 잠시 스크롤을 했다가 다시 오면 그 상태가 남아있지 않고 새로 초기화되어 원래의 상태로 돌아오게 됩니다.

그래서 이 문제를 해결하는 방법에 대해 이번 포스팅에서는 다루어 보겠습니다.


데이터 클래스 추가

우선 체크박스에 체크를 하면 그 체크한 이미지 객체에 대해 현재값을 저장해놓아야 합니다.
따라서 다음과 같은 데이서 클래스를 작성합니다.

class checkboxData(
    var id: Long,
    var checked: Boolean)


데이터 리스트 생성

데이터 클래스를 추가했으면 이제는 체크박스가 체크될 데이터 체크값을 저장할 리스트를 만들어 줍니다.

저는 저의 프로그램 구조상, companion 객체로 만들었고, 기존의 리스트 객체와 별개로 사용합니다.

이 부분은 사용자에 맞게 작성하셔서 사용하시면 될 것 같습니다. 굳이 companion으로 활용하지 않아도 되고 일반 뷰 리스트와 통합으로 사용하셔도 됩니다.

companion object {
        var list = arrayListOf<thumbnailData>()
        var checkboxList = arrayListOf<checkboxData>()
    }


어댑터 수정

어댑터의 bind 부분에 다음과 같은 코드를 추가합니다.

이 코드는 뷰가 바인딩 될 때, 체크박스리스트에 객체를 하나씩 추가해줍니다.

if(num >= checkboxList.size)
                checkboxList.add(num, checkboxData(data.photo_id, false))


아래의 코드는 체크박스 리스트에 있는 값을 현재 체크박스로 가져와서 적용합니다.
그리고 체크박스에 클릭리스너를 달아서 체크박스 리스트의 값에 업데이트합니다.

checkbox.isChecked = checkboxList[num].checked
            checkbox.setOnClickListener {
                if(checkbox.isChecked) {
                    checkboxList[num].checked = true
                }
                else {
                    checkboxList[num].checked = false
                }
            }


어댑터 종합

종합하면 어댑터 클래스 코드는 다음과 같습니다.

class RecyclerAdapterPhoto(val context: Activity?, var list: ArrayList<thumbnailData>) :
    RecyclerView.Adapter<RecyclerAdapterPhoto.Holder>()
{
    private lateinit var view: View
    private var ck = 0

    inner class Holder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
        var thumbnail: ImageView = itemView!!.findViewById<ImageView>(R.id.thumbnail_img)
        var checkbox: CheckBox = itemView!!.findViewById<CheckBox>(R.id.checkbox)

        fun bind(data : thumbnailData, num: Int) {
            val layoutParam = thumbnail.layoutParams as ViewGroup.MarginLayoutParams
            thumbnail.layoutParams.width = size
            thumbnail.layoutParams.height = size
            layoutParam.setMargins(padding_size, padding_size, padding_size, padding_size)

            if(ck == 1) {
                checkbox.visibility = View.VISIBLE
            }
            else
                checkbox.visibility = View.GONE

            if(num >= checkboxList.size)
                checkboxList.add(num, checkboxData(data.photo_id, false))


            thumbnail.setImageResource(0)


            checkbox.isChecked = checkboxList[num].checked


            checkbox.setOnClickListener {
                if(checkbox.isChecked) {
                    checkboxList[num].checked = true
                }
                else {
                    checkboxList[num].checked = false
                }
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        view = LayoutInflater.from(context).inflate(R.layout.thumbnail_imgview, parent, false)
        return Holder(view)
    }

    override fun getItemCount(): Int {
        return list.size
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.bind(list[position], position)
    }
}


결과

이제 앱을 동작시켜보면 체크된 뷰 객체가 스크롤을 하여도 그대로 체크된 상태로 남아있을 것을 확인할 수 있습니다.


Writer: Jae-Hwan Lee

댓글남기기