24.01.18 뷰모델을 인터페이스 리스너로

KSang·2024년 1월 22일
0

TIL

목록 보기
35/101

뷰모델을 인터페이스로

연락처 관련 앱을 만들고 있었는데

화면은 프래그먼트 2개로 이루어져 있고 뷰페이저로 넘겨 볼 수 있게 구성되어있다

연락처에 대한 데이터를 공유하고 있어서 공유 뷰모델로 관리하고 있었는데

뷰모델을 사용하지 말고 구축하라는 말을 듣고 새로 만들기 시작했다.

사실 처음부터 구축하는게 아닌 이미 만들어져있는걸 바꿔야한다 생각하니 난감했다

바꿔야하는 부분은 데이터가 변경됬을때 보고 있는 프래그먼트에 맞춰 리사이클러뷰가 갱신되야 한다

변경되는 데이터는 컬러가 바뀔때, 리스트의 타입이 바뀔때(Linear,Grid) 등이 있다

어떤식으로 공유 뷰모델을 대체할 수 있을까 했는데

인터페이스 리스너를 만들어서 구축하는 방법을 생각했다

인터페이스 리스너로 데이터가 변경될때 리스너를 이용해서 리사이클러뷰를 갱신해주면 될것이다

이걸 어떻게 구축할까?

프래그먼트를 보면 이런식으로 되어있는데 Contacts .. 는 기본적인 연락처 Favorite .. 는 즐겨찾기한 연락처 프래그먼트다

어댑터야 같은 타입이니 그냥 묶어주면 되지만, 프래그먼트쪽이 고민이었다.

이걸 어떻게 인터페이스로 묶어서 사용할 수 있을까

그러다 알게된건 프래그먼트의 클래스를 직접 묶는게 아니라 프래그먼트가 사용하고 있는 리사이클러뷰를 묶으면 된다는걸 알게됬다

interface BindingWithRecyclerView {
    val recyclerView: RecyclerView
}

class RecyclerViewBindingWrapper(val binding: Any) : BindingWithRecyclerView {
    //FragmentContactListBinding. FragmentFavoriteListBinding 클래스를 직접 수정하여 인터페이스를 구현할 수는 없음
    //그래서 둘을 감싸줄 래퍼 클래스를 만들어야함
    override val recyclerView: RecyclerView
        get() = when (binding) {
            is FragmentContactListBinding -> binding.rcContactList
            is FragmentFavoriteBinding -> binding.rcFavoriteList
            else -> throw IllegalArgumentException("")
        }
}

래퍼클래스를 만들어서 binding을 받아서 연락처 프래그먼트인지 즐겨찾기 프래그먼트인지 구별하고

각플래그가 가지고 있는 리사이클러뷰를 받는다

FragmentContactListBinding이면 리사이클러뷰를 binding.rcContactList으로 받아 여기에 데이터를 변화되는걸 직접 알려주면된다

interface AdapterInterface {
    fun updateColor(newColorTheme: ColorTheme)
    fun changeLayout(layoutType: LayoutType)
    fun notifyDataSetChanged()
    fun getItemViewType(position: Int): Int
}


class ContactAdapter(
    private var mItem: List<ContactViewType>,
    private var mColor: ColorTheme,
    private val tvContactList: TextView
) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable, AdapterInterface {
        
        ...

        override fun updateColor(newColorTheme: ColorTheme) {
        mColor = newColorTheme
        notifyDataSetChanged()
    }
    ...

어뎁터 같은 경우 이런식으로 인터페이스를 만들어서 묶어줬다

이제 데이터를 변경할때쓸 리스너를 만들어주기만하면된다

interface OnDataChangedListener {
    //리사이클러뷰 색변경
    fun onColorChanged(color: ColorTheme)
    //리사이클러뷰 타입변경
    fun onLayoutTypeChanged(type: LayoutType)
    //리사이클러뷰 데이터의 타입변경
    fun onLayoutType(type: LayoutType)
}

...

class DataChangedListener(private val adapter: AdapterInterface?, private val binding: RecyclerViewBindingWrapper) : OnDataChangedListener {
    override fun onColorChanged(color: ColorTheme) {
        adapter?.updateColor(color)
    }

    override fun onLayoutTypeChanged(type: LayoutType) {
        val context = binding.recyclerView.context
        when (type) {
            LayoutType.GRID -> {
                val gridManager = GridLayoutManager(context, 4)
                gridManager.spanSizeLookup = object :SpanSizeLookup() {
                    override fun getSpanSize(position: Int): Int {
                        return when (adapter?.getItemViewType(position)){
                            0 -> 1
                            else -> 4
                        }
                    }

                }
                binding.recyclerView.layoutManager = gridManager
            }
            else -> {
                binding.recyclerView.layoutManager = LinearLayoutManager(context)
            }
        }
    }

    override fun onLayoutType(type: LayoutType) {
        adapter?.changeLayout(type)
    }
}

어뎁터와 리사이클러뷰 래퍼를 파라미터로 받는 리스너를 만들어서

데이터가 변경될때 어댑터에 바로 변화를 주도록 만들었다.

0개의 댓글