안드로이드/오류, 삽질 및 해결방법

[Android/Kotlin/해결 ...?] 리사이클러뷰 mvvm 적용 관련 삽질

감자 바보 2022. 1. 11. 23:15
반응형

레포지토리 코드

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)

 

코틀린 두 list 값 비교 == 연산자

자바에서 두 list의 값을 비교할 때 반복문을 돌려도 되지만 listA.containsAll(listB) 처럼 containsAll메소드를 사용하면 두 리스트의 요소들이 일치하는지 판단해서 true or false를 반환해줬습니다. 코틀린

altongmon.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
    }
}

느낀 점

코틀린과 안드로이드에 대한 이해가 부족한 것을 확인할 수 있었다. 코틀린 기초 지식을 쌓는 과정을 겪어야할 것 같다.