[Android, Kotlin] 외부에서 Recycler View Adapter Position 변경시 원하지 않는 데이터까지 변경되는 문제

[문제 상황]

 

일정 관리 앱을 개발 중이었다.

캘린더에서 item을 클릭하면 해당 일자의 todo List가 있는  slide view가 나타나고,

해당 view에서 data를 추가하면 캘린더에 (일정 있음)icon을 나타내는 방식의 코드를 작성했다.

todo List에 데이터가 있으면 캘린더의 item에 icon을 visible 해주고 없다면 invisible 해야했다.

 

하지만 데이터를 추가하거나 삭제하면 해당 position의

item이 아닌 엉뚱한 item의 데이터들까지 변경되는 문제가 발생했다.

엉뚱한 position에 데이터가 변경되는 것 뿐아니라 기존에 설정 된 글자색 등의 

dataSet[position]에 해당하는 값이 아닌 다른 값들이 변경 되어서 원하는 view가 그려지지 않았다.

 

//오늘 날짜 + 현재 달인 경우만 진하게
if (dataSet[position]==dateDay.toInt() && currentMonth ==dateMonth){
    holder.binding.calendarText.apply {
        setTypeface(this.typeface, Typeface.BOLD_ITALIC)
    }
}
//다른 달인 경우 회색으로 표시
if (position<firstDateIndex || position>lastDateIndex){
    holder.binding.calendarText.apply {
        setTextAppearance(R.style.grayColorText)
    }
}
//달력에 일정 아이콘 표시 여부
val key = dateTime+" "+ holder.binding.calendarText.text.toString()+"일"

if (mainActivity.viewModel.todoData[key]!=null) {
    holder.binding.isItTodo.visibility = View.VISIBLE
}

 

Adapter의 onBindViewHolder에 위와 같은 코드를 작성했다.

처음 뷰가 그려질 때는 내가 원하는대로 3가지 옵션이 모두 적용 되었으나,

데이터를 추가해서 icon을 변경, notifyItemChanged 실행 후에는 원하지 않은 position의 글자값이 변하던지

일정이 없는 position의 item에 icon이 생기는 등 원하지 않는 결과를 얻었다.

 

 

[해결방법]

 

onBindViewHolder는 생성된 뷰홀더에 데이터를 바인딩 해주는 함수이다.

 

데이터가 스크롤 되어서 맨 위에있던 뷰 객체가 맨 아래로 이동하는 등의

레이아웃이 재사용 된다면 데이터의 구조가 새로 바뀌게 된다.

 

즉 새롭게 보여질 데이터에 대한 position은 view가 새로 그려질 때마다 변경되는 것이다.

slide view를 사용하고 다시 캘린더로 돌아오거나, notifyItemChanged()가 실행될 때 기존의 position과

값이 다르다면 내가 추가한 값과 다른 position에 icon이 그려지고, position의 변화로 글자색 부분도 다르게 변하는 것 같다.

 

내가 생각한 해결 방법은 dataSet의 값에 3가지 if문에 대한 조건을 변수로 만들어서 저장하고

뷰에서 데이터의 position에 접근할 때는 해당 position 안에 있는 조건을 변수로 만든 값을 확인해서 적용시키는 방법이다.

 

간단하게 말해서 아래와 같이 CustomDayDate에 3가지 Boolean (+ 1가지는 아직 구현 안함)을 만들어주고,

dataSet에 위에서 설정한 조건들을 변수로 만들어서 position에 접근 시

이 데이터들이 어떤 조건이 적용되는지 확인하는 것이다.

 

class CustomDayData(
    var day :Int,
    var isTodoData :Boolean,
    var inRepeatData : Boolean,
    var isToday : Boolean,
    var isOtherMonth : Boolean
):Serializable

 

day는 현재 달의 일을 뜻한다.

isToday는 오늘을 뜻하며  True 시 위의 조건에서 글자색을 진하게 만드는 조건이다.

isRepeatData는 아직 구현하지 않았다.

isTodoData는 todo 데이터가 있는지를 뜻하며 True 시 위의 조건에서 icon을 visible한다.

isOtherMonth는 현재 일자가 다른 달의 일자인지를 뜻하며 True 시 위의 조건에서 글자색을 흐리게 만드는 조건이다.

 

dataSet을 생성할 때 위의 데이터들이 True인지 False인지 설정해주면 

어차피 dataSet 생성 시 for문을 돌면서 데이터를 추가하므로 또다시 조건을 검색할 필요도 없어졌다.

 

onBindViewHolder에서 수정한 코드는 아래와 같다.

//오늘 날짜 + 현재 달이 오늘 : 진하게
if (dataSet[position].isToday) {
    holder.binding.calendarText.apply {
        setTypeface(this.typeface, Typeface.BOLD_ITALIC)
    }
}

//다른 달 : 회색으로 표시
if (dataSet[position].isOtherMonth) {
    holder.binding.calendarText.apply {
        setTextAppearance(R.style.grayColorText)
    }

}

//일정 존재
if (dataSet[position].isTodoData){
    holder.binding.isItTodo.visibility = View.VISIBLE
}else{
    holder.binding.isItTodo.visibility = View.INVISIBLE
}

 

이제 정상적으로 icon 설정이 완료되었다!

 

 

 

[또 다른 문제]

 

icon은 정상적으로  변경 되었지만 text view의 색상이나 진하기를

변경하는 부분에서 전보다는 덜하지만 가끔 문제가 생기고 있다.

 

값을 출력해보고 데이터를 확인해봐도 아무런 문제가 없는데,

아무래도 setText나 setType를 적용하는데 있어서 다른 문제가 있는것 같았다.

 

일단 중요한 기능은 아니므로 넘어가고 다른 방법을 찾아봐야겠다..