레포지토리 코드
class LeagueDateRepository {
val _leagueDateArray = MutableLiveData<List<LeagueDate>>()
fun initRepository(array: List<LeagueDate>) {
_leagueDateArray.value = array
if (_leagueDateArray.value!!.size > 0)
_leagueDateArray.value!![0].isSelected = true
}
fun dateClickCallback(oldPosition: Int, newPosition: Int) {
if (oldPosition == newPosition)
_leagueDateArray.value!![newPosition].isSelected = true
else {
_leagueDateArray.value!![oldPosition].isSelected = false
_leagueDateArray.value!![newPosition].isSelected = true
}
}
}
뷰모델 코드
class HomeViewModel : ViewModel() {
private val leagueDateRepository = LeagueDateRepository()
val leagueDateData: LiveData<List<LeagueDate>> get() = leagueDateRepository._leagueDateArray
fun initRepository(array: ArrayList<LeagueDate>) {
leagueDateRepository.initRepository(array)
}
fun dateClickCallback(oldPosition: Int, newPosition: Int) {
leagueDateRepository.dateClickCallback(oldPosition, newPosition)
}
}
프래그먼트 코드
leagueDateList = ArrayList<LeagueDate>()
leagueDateAdapter = LeagueDateAdapter(clickCallback = { old, new ->
homeViewModel.dateClickCallback(old, new)
Log.d("dateCallback", homeViewModel.leagueDateData.value.toString())
leagueDateAdapter.submitList(homeViewModel.leagueDateData.value!!.toMutableList())
})
var l = LinearLayoutManager(requireContext())
l.orientation = LinearLayoutManager.HORIZONTAL
binding.leagueDateRecyclerView.layoutManager = l
binding.leagueDateRecyclerView.adapter = leagueDateAdapter
homeViewModel.leagueDateData.observe(viewLifecycleOwner, Observer {
leagueDateAdapter.submitList(homeViewModel.leagueDateData.value)
})
문제 원인(추정)
LiveData 내부에 리스트 넣을 경우 일반적인 깊은 복사 방식으로는 얕은 복사만 되는 것을 확인하였다.
+ 추가) 코틀린에서는 리스트 계열의 경우 ==연산자 동작이 내부 element를 비교한다고 한다. 객체 리스트의 경우 기존 방식으로 복사할 경우 내부 객체의 주소가 동일하니까 얕은 복사로 취급 되는 것 같다.
출처 : IOS를 Java :: 코틀린 두 list 값 비교 == 연산자 (tistory.com)
이 문제 때문에 옵저버와 submitList가 동작을 안하는 것 같다. 문제 해결 방법을 찾아봤지만 정보를 찾을 수 없었다.
LiveData<List>같은 경우는 리스트의 원소 값을 확인하는 것이 아닌 리스트 주소의 변경 여부를 확인하는 것 같다.
삽질 (실패 기록)
1. 내부 객체를 새로운 객체로 교체하여 해결 시도
var temp = ArrayList<LeagueDate>()
homeViewModel.leagueDateData.value!!.forEach {
temp.add(LeagueDate(it.startDate, it.isSelected))
}
if (homeViewModel.leagueDateData.value!! == temp)
Log.d("dateCallback", "same")
=> same이 출력되며 해결 실패
해결...? 방법 (2022.01.12 추가)
변경사항이 생길 때마다 객체 리스트를 깊은 복사하여 MutableLiveData의 value인 ArrayList를 교체하여 해결하였다.
LiveData<List>같은 경우는 리스트의 원소 값을 확인하는 것이 아닌 리스트 주소의 변경 여부를 확인하는 것 같다. 따라서 변경상황이 생겼을 때 우선 기존 리스트를 깊은 복사하여 임시 리스트를 생성하고 임시 리스트를 변경한 뒤 MutableLiveData의 value를 임시 리스트로 변경해주는 식으로 observer를 원하는 대로 사용할 수 있었다.
class LeagueDateRepository {
val _leagueDateArray = MutableLiveData<ArrayList<LeagueDate>>()
fun initRepository(array: ArrayList<LeagueDate>) {
_leagueDateArray.value = array
if (_leagueDateArray.value!!.size > 0)
_leagueDateArray.value!![0].isSelected = true
}
fun dateClickCallback(oldPosition: Int, newPosition: Int) {
val temp = ArrayList<LeagueDate>()
_leagueDateArray.value!!.forEachIndexed { index, item ->
if (index == newPosition)
temp.add(LeagueDate(item.startDate, true))
else if (index == oldPosition)
temp.add(LeagueDate(item.startDate, false))
else
temp.add(LeagueDate(item.startDate, item.isSelected))
}
_leagueDateArray.value = temp
}
}
느낀 점
코틀린과 안드로이드에 대한 이해가 부족한 것을 확인할 수 있었다. 코틀린 기초 지식을 쌓는 과정을 겪어야할 것 같다.
'안드로이드 > 오류, 삽질 및 해결방법' 카테고리의 다른 글
[Android] 안드로이드 스튜디오 2021.2.1 버전 chipmunk firebase realtime-database gradle 설정 (2) | 2022.07.24 |
---|---|
[Android/Kotlin] Live Data 테스트 오류 (Method getMainLooper in android.os.Looper not mocked.) (0) | 2022.04.14 |
[Android/Kotlin] Koin 라이브러리 설정 문제 (0) | 2022.04.08 |
[Android/Kotlin] Navigation Safe Args 에러 해결 (0) | 2022.02.17 |
[안드로이드] MPAndroidChart 라이브러리 gradle 설정 에러 (0) | 2022.01.09 |