개발일지

Android in A..Z - RecyclerView (setHasStableIds) 본문

Android (안드로이드)/RecyclerView

Android in A..Z - RecyclerView (setHasStableIds)

강태종 2021. 1. 10. 18:09

setHasStableIds

Adapter에 Item들이 고유한 ID값을 가진다고 Adapter에 설정하는 함수이다. setHasStableIds 설정을 통해 ViewHolder에 onBindViewHolder를 필요할 때만 호출하여 불필요한 리소스를 아낄 수 있다.

 


ToDoAdapter

setHasStableIds를 사용하면 getItemId를 구현해야한다. 또한 getItemId는 고유한 id를 리턴하도록 설정해야하며 중복된 id가 있을경우 Exception을 발생시킨다.

class ToDoAdapter : BaseAdapter<ToDo>(ToDoItemCallback()) {
    init {
        setHasStableIds(true)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseHolder<out ViewDataBinding, ToDo> {
        return ToDoHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), viewType, parent, false))
    }

    override fun getItemId(position: Int): Long {
        return getItem(position).id
    }

    override fun getItemViewType(position: Int): Int {
        return R.layout.holder_todo
    }

    inner class ToDoHolder(binding: HolderTodoBinding) : BaseHolder<HolderTodoBinding, ToDo>(binding) {
        private var isFinished by Delegates.observable(false) { _, _, newValue ->
            if (newValue) {
                with(binding.isFinished) {
                    animate()
                            .withStartAction {
                                visibility = View.VISIBLE
                            }
                            .alpha(1F)
                            .scaleX(1F)
                            .scaleY(1F)
                            .setDuration(500)
                            .setInterpolator(OvershootInterpolator())
                }
            } else {
                with(binding.isFinished) {
                    animate()
                            .alpha(0F)
                            .scaleX(0F)
                            .scaleY(0F)
                            .setDuration(500)
                            .withEndAction {
                                visibility = View.GONE
                            }
                }
            }
        }

        init {
            itemView.setOnClickListener {
                this@ToDoAdapter.notifyItemChanged(adapterPosition, "onClick")
            }
        }

        private fun onClick() {
            isFinished = !isFinished
        }

        private fun onRefresh() {
            isFinished = false
        }

        override fun bind(element: ToDo) {
            super.bind(element)
            binding.todo = element
        }

        override fun bind(element: ToDo, payload: MutableList<Any>) {
            super.bind(element, payload)
            for (any in payload) {
                when(any) {
                    "onClick" -> {
                        onClick()
                    }
                    "onRefresh" -> {
                        onRefresh()
                    }
                }
            }
        }
    }

    private class ToDoItemCallback : DiffUtil.ItemCallback<ToDo>() {
        override fun areItemsTheSame(oldItem: ToDo, newItem: ToDo): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: ToDo, newItem: ToDo): Boolean {
            return oldItem.text == newItem.text
        }
    }
}

예시

변경 전 => 변경 후
position id position id
0 0 0 5
1 1 1 1
2 2 2 2

ViewHolder에 bind된 데이터가 바꾸려는 데이터와 id값이 일치하면 onBindViewHolder를 호출하지 않으면서 리소스를 아낄 수 있다. => position 0번은 onBindViewHolder가 호출되고 position 1, 2번은 onBindViewHolder가 호출되지 않는다.


Git (예제코드)

github.com/KangTaeJong98/Example/tree/main/Android/RecyclerView

 

KangTaeJong98/Example

My Example Code. Contribute to KangTaeJong98/Example development by creating an account on GitHub.

github.com

 

Comments