Android

[Android, Kotlin] RecyclerView에서 스크롤 시 Check box의 check 상태 보존

배경 : "편집" 버튼 클릭 시, RecyclerView의 각 Item 왼쪽에 선택 할 수 있는 체크 박스가 나온다. 

 

문제 : 체크 후, RecyclerView 스크롤 시 체크의 상태가 보존되어 있지 않는다.

 

기존 코드 :

 

// ViewHolder의 bind 메소드
fun bind(info: TrackingInfoCompany) {
    val position = bindingAdapterPosition.takeIf { it != NO_POSITION } ?: return

    binding.information = info
    binding.checkBox.isVisible = modifyMode

    binding.checkBox.isChecked = getItem(position).isChecked

    binding.checkBox.setOnCheckedChangeListener { _, isChecked ->
        getItem(position).isChecked = isChecked
    }

    if (info.info!!.completeYN == "Y") {
        binding.dateTv.setTextColor(ContextCompat.getColor(binding.root.context,
            R.color.divider_gray))
    }
}

 

checkBox의 초기 check값을 객체의 isChecked 여부에 따라 설정하고, checkBox에 listener를 설정하여 check 여부가 바뀔 때마다 객체에 checked 여부를 업데이트 한다.

 

이론 상으로는 아무 문제 없이 잘 동작해야 할 것 같으나, 문제는 RecyclerView의 특성에 있다.

 

1. RecyclerView는 화면에 보여지는 뷰만이 유효한 값을 가지며,

2. 이마저도 기존의 뷰를 재활용하는 특성을 가지고 있다.

그러므로, 체크한 후 스크롤을 하여 화면에서 사라지게 되면 check box의 값은 더 이상 유효해지지 않는다.

1과 같은 특성 때문에 객체에 isChecked 값을 저장해놓고 check box의 초기값을 설정하였다. 

그러나 기존의 뷰를 재활용하므로, 설정한 checkBox의 listener 역시 recycler view의 recycle에 의해 충돌된다.

 

이를 위해, bind 메소드에서 기존 listener를 null로 초기화 하고, 다시 설정하는 과정을 거쳐야 한다.

 

 

해결 코드 : 

 

fun bind(info: TrackingInfoCompany) {
    val position = bindingAdapterPosition.takeIf { it != NO_POSITION } ?: return

    binding.information = info
    binding.checkBox.isVisible = modifyMode

    binding.checkBox.setOnCheckedChangeListener(null) // 기존 listener를 null로 초기화하여 충돌 방지

    binding.checkBox.isChecked = getItem(position).isChecked

    binding.checkBox.setOnCheckedChangeListener { _, isChecked ->
        getItem(position).isChecked = isChecked
    }

    if (info.info!!.completeYN == "Y") {
        binding.dateTv.setTextColor(ContextCompat.getColor(binding.root.context,
            R.color.divider_gray))
    }
}