Recyclerview With Databinding
Why use it?
How many times setText is invoked in your onBindViewHolder? For complex recycler items it can be really annoying. You have several lines to set texts, images etc. Databinding allows us shorten this stuff.
Gradle setup
First, enable databinding in module-level build.gradle:
android{
....
dataBinding {
enabled = true
}
}
Also add databinding annotation processor
dependencies{
compile 'com.android.databinding:compiler:${android_plugin_version}'
}
RecyclerView item ViewModel
Consider ViewModel as structure with all data you display. It will be very similar to your API pojos or database entities. So what’s the difference?
In our API we have users first and last name in separate fields.
Maybe it’s convenient for backend database,
maybe
At this point we should have ItemViewModel class created. Now let’s bind some data.
Binding data in XML
Wrap all layout definition in
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.rozkmin.recyclerdatabindingshowcase.ItemViewModel"/>
</data>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@{item.me ? @drawable/item_background_highlight : @drawable/item_background_normal}"
tools:background="@drawable/item_background_highlight"
android:layout_margin="12dp"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_gravity="start|center"
android:text="@{item.position}"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
tools:text="1."/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_gravity="center"
android:text="@{item.name}"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="Edward Dolański"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_gravity="end|center"
android:text="@{item.team}"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="Super Team"/>
</FrameLayout>
</layout>
As you see each ViewModel property was assigned in xml with @{item.property}. We also provided and
Interesting thing is:
android:background="@{item.me ? @drawable/item_background_highlight : @drawable/item_background_normal}"
It is possible to create simple if-else logic in layouts. We are using it to change style and highlight current user row. Way more readable than writing several ‘ifs’ in RecyclerView.Adapter class
Bind data
class ItemAdapter : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
var items = listOf<ItemViewModel>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) =
holder.bind(items[position])
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ItemViewHolder =
ItemViewHolder(ItemBinding.inflate(LayoutInflater.from(parent?.context), parent, false))
override fun getItemCount(): Int = items.size
inner class ItemViewHolder(private val itemBinding: ItemBinding) : RecyclerView.ViewHolder(itemBinding.root) {
fun bind(itemViewModel: ItemViewModel) {
itemBinding.item = itemViewModel
itemBinding.executePendingBindings()
}
}
}
We create ItemViewHolder with ItemBinding - class generated from item.xml. If you don’t see binding class in your classpath, try to rebuild project. Interesting thing is happening in method bind():
fun bind(itemViewModel: ItemViewModel) {
itemBinding.item = itemViewModel //assign our item instance to generated class
itemBinding.executePendingBindings() //similar to notifyDataSetChanged()
}