[Android, Kotlin] Recycler View에서 원하지 않는 position의 데이터가 변경되는 문제 해결

 

[문제상황]

 

한 달 주기 일정 관리 앱을 개발 중이었다.

이전에 원하지 않는 position의 데이터가 변경되는 문제를 해결하기 위해서

dataSet에 따로 Boolean 값을 둬서 dataSet의 Boolean 값에 따라 색상을 다르게 하는 코드를 작성했다.

 

하지만 여전히 원하지 않는 position의 데이터가 변경되는 문제가 발생했고 이를 해결하기 위한 방법이 필요했다.

 

[문제 해결]

 

우선 RecyclerView의 ViewHolder에 관련된 개발 문서를 읽어보았다.

 

RecyclerView.ViewHolder  |  Android Developers

 

RecyclerView.ViewHolder  |  Android Developers

androidx.car.app.managers

developer.android.com

 

여기서 아래의 getAbsoluteAdapterPosition에 대한 설명을 읽었다.

 

public final int getAbsoluteAdapterPosition()

 

리사이클러뷰의 어댑터와 관련해서 뷰 홀더가 나타내는 아이템의 어댑터 위치를 반환한다.

즉 ViewHolder를 binding 한 어댑터가 다른 어댑터 안에 있는 경우 위치가 다를 수 있으며

서로 다른 어댑터로 인하여 오프셋을 포함한다.

 

리싸이클러뷰에서의 position과 현재 보고 있는 view의 position이 다를 수도 있다는 것이다.

이를 해결하기 위해서는 getAdapterPosition()을 사용하였는데,

이는 departed 되고 getAbsoluteAdaperPosition를 대신 사용한다.

 

onBindViewHolder에서 position은 다른 값을 나타낼 수 있으므로

ItemHolder 내부에서 getAbsoluteAdapterPosition을 통해서 데이터를 접근하는 코드를 짜야했다.

 

onBindViewHolder에서는 textView나 ImageView의 데이터를 초기화해주는 코드만 작성하고 ItemHolder로 오류가 발생하는 색상을 변경하는 코드를 이동했다.

 

기본 값을 초기화해주는 코드는 그대로 onBindViewHolder에 남겨두었다.

ItemHolder에 bind 함수를 만든 후에 포지션의 데이터를 넘겨준다.

 

override fun onBindViewHolder(holder: CalendarListItemHolder, position: Int) {
    holder.bind(dataSet[position])
    holder.binding.itemHoDayNum.text = dataSet[position].day.toString()
    holder.binding.itemHoDayText.setText(dayOfWeek[(position%7)])
}

 

여기서 중요한 점은 ItemHolder는 inner 클래스로 변경해 주어야 한다. dataSet이나 selectedPosition 등 내부 데이터를 사용하기 위해서이다.

 

bind함수로 onBindViewHolder에서 데이터를 item으로 받아온다. 이제 받아온 데이터를 통해서 해당 데이터에 대한 접근을 할 수 있다.

 

bind 함수 내부에서 absoluteAdapterPosition으로 DataSet에 맞는 position에 접근할 수 있다.

 

inner class CalendarListItemHolder(val binding: ItemCalendarHorizontalBinding) :
    RecyclerView.ViewHolder(binding.root) {
    fun bind(item: DayData) {

        binding.root.setOnClickListener {

            if (selectedPosition != absoluteAdapterPosition) {
                binding.setChecked()
                notifyItemChanged(selectedPosition)
                selectedPosition = absoluteAdapterPosition
            }
        }

    }
}

 

결론은 onBindViewHolder에서는 초기화할 때의 코드만 작성하고,

item Click이나 특정 item에 대한 접근하기 위해서는 ItemHolder의 absoluteAdapterPosition을 통해서

본래의 position에 접근해야 한다는 것이다.

 

onBindViewHolder의 position은 접근 시 다를 수 있고,

absoluteAdapterPosition의 position은 dataSet에 맞춰진 절댓값 포지션이다.