今天來看看 Data Binding 裡的 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 是谷歌在 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 的時候要記得轉型。
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