[Android] 2022 rồi, hãy tạm quên RecyclerView.Adapter đi.

[Android] 2022 rồi, hãy tạm quên RecyclerView.Adapter đi.

Bước tiến mới trong việc xử lý list dữ liệu cho recycler view

Tại Google I/O 2018, ListAdapter đã được giới thiệu, cùng với DiffUtils và hàng loạt các thư viện jetpack được cho ra đời khi đó, có thể nói google đã đưa ra một giải pháp cứu cánh rất hợp lý lúc bấy giờ dành cho những chiếc app nặng về xử lý giao diện list data.

DiffUtil

object PlayerDiffUtil : DiffUtil.ItemCallback<PlayerModel>() {
        override fun areItemsTheSame(oldItem: PlayerModel, newItem: PlayerModel) =
            oldItem.id == newItem.id

        override fun areContentsTheSame(oldItem: PlayerModel, newItem: PlayerModel) =
            oldItem.id == newItem.id
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder.create(parent)

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.initView()
        holder.bindData(getItem(position))
    }
Diff util callback

Kể từ bây giờ chúng ta không cần phải gọi những "notifyDataSetChanged"... mỗi khi load lại list hay có sự thay đổi về các item bên trong. DiffUtil sẽ tự động làm việc đó thay chúng ta bằng việc check lần lượt dựa vào điều kiện mà người dùng khai báo qua 2 phương thức areItemsTheSame và areContentsTheSame (nếu trả về false thì recyclerview sẽ load lại list).

ListAdapter

Nhân vật chính của chúng ta khá mạnh mẽ so với phiên bản trước đây của nó (RecyclerView.Adapter)

class PlayerAdapter : ListAdapter<PlayerModel, PlayerAdapter.ViewHolder>(PlayerDiffUtil) {

    object PlayerDiffUtil : DiffUtil.ItemCallback<PlayerModel>() {
        override fun areItemsTheSame(oldItem: PlayerModel, newItem: PlayerModel) =
            oldItem.id == newItem.id

        override fun areContentsTheSame(oldItem: PlayerModel, newItem: PlayerModel) =
            oldItem.id == newItem.id
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder.create(parent)

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.initView()
        holder.bindData(getItem(position))
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun initView() {
            //init view
        }

        fun bindData(player: PlayerModel) {
            //bind data to view
        }

        companion object {
            fun create(parent: ViewGroup) = ViewHolder(
                LayoutInflater.from(parent.context)
                    .inflate(R.layout.item_constraint_csgo_player, parent, false)
            )
        }
    }
}
Cách khai báo ListAdapter

Giờ đây chúng ta không cần phải khai báo item count và getter/setter cho một biến list data như cách truyền thống nữa, load data được thực hiện bởi một dòng

playerAdapter.submitList(dataList)

Ưu điểm so với RecyclerView.Adapter

- Khi chuyển màn hình sau đó back lại thì list không bị nháy (đối với mình thì đây là thứ đáng tiền nhất).
- Tối ưu performance vì không cần phải gọi notifyDataSetChanged nữa (vì khi notify thì toàn bộ view sẽ buộc phải render lại dẫn đến hiệu năng bị bóp cực mạnh)
- Khai báo ngắn gọn.

Demo

Ref:
Bài viết dựa theo kinh nghiệm thực tế của bản thân, tuy nhiên mình cũng recommend tới các bạn những nguồn uy tín sau:
https://developer.android.com/reference/androidx/recyclerview/widget/ListAdapter
https://medium.com/geekculture/android-listadapter-a-better-implementation-for-the-recyclerview-1af1826a7d21.