iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0

今天來看看 Data Binding 裡的 RecyclerView 如何使用

前提

RecyclerView 是什麼

先前用 Data Binding 繪製 Layout 都是在 Activity

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

那如果不是在 Activity 要怎麼繪製呢,Databinding 還有另一種方法可以繪製 Layout

val rootView = LayoutInflater.from(this).inflate(R.layout.activity_main, null)
binding = ActivityMainBinding.bind(rootView)
setContentView(binding.root)

(效果一樣)
透過 binding.root 可以拿到 Databinding 幫我們自動生成的 xml 檔
懂得這個原理後我們就可以應用在其他地方

Fragment:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

    binding = FragmentProfileBinding.inflate(inflater, container, false)
    return binding.root
}

RecyclerView.ViewHolder

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ItemMyProfileBinding.inflate(layoutInflater, parent, false)
        return ProfileHolder(binding)
    }

ListAdapter

ListAdapter 是谷歌在 2018 新增的一個用來優化 RecyclerView.Adapter 的類,能夠用更簡潔的代碼完成 Adapter,並且加入了 DiffUtil 這個輔助類,大大優化了 RecyclerView 的效能。

使用

class PlayerAdapter : ListAdapter<DataItem, RecyclerView.ViewHolder>(DiffCallback) {

原本繼承 Adapter 改成繼承 ListAdapter,裡面放入想要丟給 ViewHolder 的 Item 以及 ViewHolder

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ItemPlayerProfileBinding.inflate(layoutInflater, parent, false)
        return PlayerHolder(binding)
    }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

        when (holder) {
            is PlayerHolder -> {
                holder.setResult(getItem(position) as DataItem)
            }
        }
    }

這裡已經不需要在重寫 getItemCount 了,那就會想問:那我要顯示的 List 要在哪邊丟進來?
這個後面一點會補上。

onCreateViewHolder 裡面的 layout 就用之前 databinding 繪製 layout 的第二種方法
onBindViewHolder 裡面一樣判斷是哪一種 holder 再決定要丟什麼數據給他,這邊的 getItem 拿到的是一個泛型,所以丟給 ViewHolder 的時候要記得轉型。

ViewHolder

class PlayerHolder(private val binding: ItemPlayerProfileBinding) : RecyclerView.ViewHolder(binding.root) {

    fun setResult(dataItem: DataItem) {

        binding.dataItem = dataItem
        binding.executePendingBindings()
    }
}

把 binding 丟過來之後就可以透過 binding.root 告訴 ViewHolder 這個 holder 的 layout 長什麼樣子
然後就把數據跟 binding 綁在一起就可以了,
executePendingBindings()非常重要
executePendingBindings()非常重要
executePendingBindings()非常重要
,加上這句之後數據每次丟進來時才能夠即時更新

回到剛剛

class PlayerAdapter : ListAdapter<DataItem, RecyclerView.ViewHolder>(DiffCallback) {

這個 DiffCallback 就是就是用來優化 RecyclerView 效能的工具

   companion object DiffCallback : DiffUtil.ItemCallback<DataItem>() {

        override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
            return oldItem === newItem
        }
        override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
            return oldItem.userId == newItem.userId
        }
    }

他繼承了 DiffUtil.ItemCallback裡面重寫了兩個方法

areItemsTheSame
檢查兩個物件是否是同一個對象(記憶體位置相同),如果是,則不做任何操作,如果不是,則更新這個 Item。

areContentsTheSame
檢查某一個成員變數的值是否一樣,,如果是,則不做任何操作,如果不是,則更新這個 Item。
這裡可以依需求自行更換其他成員變數。

透過這兩個方法可以決定 RecyclerView 的加載機制,而且可以根據需求來做變化,相當彈性。

到這邊已經算是大致完成了,最後剩下把 List 丟給 RecyclerView 這個步驟,因為要用到 databinding
所以綁定的部分一定都是在 xml

<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:playerItems="@{viewModel.dataItemList}" />

這邊的 playerItems 是我們自定義的,沒錯,就是之前使用的 BindingAdapter

@BindingAdapter("playerItems")
fun bindRecyclerViewWithDataItemList(recyclerView: RecyclerView, dataItemList: List<DataItem>?) {
    dataItemList?.let {
        recyclerView.adapter?.apply {
            when (this) {
                is PlayerAdapter -> submitList(it)
            }
        }
    }
}

submitList 就是 ListAdapter 裡面的方法,用來提交所要顯示的 List,大功告成。

有任何問題或講得不清楚的地方歡迎留言和我討論。

更歡迎留言糾正我任何說錯的地方!

下一篇:Data Binding (Last) 雙向綁定 InverseBindingAdapter


上一篇
Day 12 Data Binding (五) Observable
下一篇
Day 14 Data Binding (Last) 雙向綁定 InverseBindingAdapter
系列文
Android Architecture Components 學習心得筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言