Every Android developer knows the workflow how to implement a RecyclerView: Creating a ViewHolder, an adapter and finally apply it to the RecyclerView. Imho it is quite cumbersome to invest so much overhead for a (from the view point of a user) "simple" and (from the view point of a developer) often used UI component like RecyclerViews.

Data Binding

An additional feature in Android development was the introduction of the Data Binding Library at Google I/O 2015 to remove boilerplate code. Data Binding uses declarative layouts and minimises the glue code between programming code and XML. For those who are not yet familiar with the Data Binding Library, check out the official Android developer docs: https://developer.android.com/topic/libraries/data-binding/index.html.

Although Data Binding was already introduced in 2015, there is still no Data Binding extension for lists and RecyclerViews. Wouldn’t it be nice if Data Binding can be used for RecyclerViews?

Thank goodness Evan Tatarka implemented a library to apply data binding for lists and RecyclerViews, called the Binding Collection Adapter: https://github.com/evant/binding-collection-adapter.

In the upcoming sections, I show you how to implement a RecyclerView using data binding with the MVVM architectural pattern. I selected MVVM because it works smoothly with data binding.

The Binding Collection Adapter library

Applying the Binding Collection Adapter library in your project is straightforward.

Beside the RecyclerView and data binding dependencies and configurations, you have to add the library itself.



build.gradle implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:2.2.0' implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:2.2.0' 1 2 implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter:2.2.0' implementation 'me.tatarka.bindingcollectionadapter2:bindingcollectionadapter-recyclerview:2.2.0'

Entry Layout

Like in the ViewHolder-Adapter-RecyclerView approach, we need to define a layout for our list entries. This time we are using data binding.



layout/text_entry.xml <data> <variable name="item" type="Object"/> <variable name="name" type="String"/> <variable name="value" type="String"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView style="@style/Text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@{name}"/> <TextView style="@style/Text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@{value}"/> </LinearLayout> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <data> <variable name = "item" type = "Object" /> <variable name = "name" type = "String" /> <variable name = "value" type = "String" /> </data> <LinearLayout android : layout_width = "match_parent" android : layout_height = "wrap_content" android : orientation = "horizontal" > <TextView style = "@style/Text" android : layout_width = "0dp" android : layout_height = "match_parent" android : layout_weight = "1" android : text = "@{name}" /> <TextView style = "@style/Text" android : layout_width = "0dp" android : layout_height = "match_parent" android : layout_weight = "1" android : text = "@{value}" /> </LinearLayout>

ViewModel

The ViewModel is the glue code to bind the entry layout to a collection entry.



ViewModel and its binding object class DetailViewModel(val station: GasStation) { val businessHoursBinding = ItemBinding.of<BusinessHours> { itemBinding, _, item -> itemBinding.set(BR.item, R.layout.text_entry) .bindExtra(BR.name, item.days) .bindExtra(BR.value, item.time) } } data class GasStation(val businessHours: List<BusinessHours>? = null) data class BusinessHours(val days: String, val time: String) : Serializable 1 2 3 4 5 6 7 8 9 10 11 12 class DetailViewModel ( val station : GasStation ) { val businessHoursBinding = ItemBinding . of <BusinessHours> { itemBinding , _ , item -> itemBinding . set ( BR . item , R . layout . text_entry ) . bindExtra ( BR . name , item . days ) . bindExtra ( BR . value , item . time ) } } data class GasStation ( val businessHours : List <BusinessHours> ? = null ) data class BusinessHours ( val days : String , val time : String ) : Serializable

View

The ViewModel is now announced to the view. The data is introduced by the app:items property via data binding to the declared RecyclerView. The description how the data shall be mapped to the entry layout is defined by app:itemBinding. A usual app:layoutManager must be declared as well.



layout/detail_activity.xml <data> <import type="info.novatec.sprit.model.Fuel"/> <import type="android.view.View"/> <import type="me.tatarka.bindingcollectionadapter2.LayoutManagers"/> <variable name="viewModel" type="de.novatec.sprit.DetailViewModel"/> </data> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:itemBinding="@{viewModel.businessHoursBinding}" app:items="@{viewModel.station.businessHours}" app:layoutManager="@{LayoutManagers.linear()}"/> 1 2 3 4 5 6 7 8 9 10 11 12 13 <data> <import type = "info.novatec.sprit.model.Fuel" /> <import type = "android.view.View" /> <import type = "me.tatarka.bindingcollectionadapter2.LayoutManagers" /> <variable name = "viewModel" type = "de.novatec.sprit.DetailViewModel" /> </data> <android.support.v7.widget.RecyclerView android : layout_width = "match_parent" android : layout_height = "match_parent" app : itemBinding = "@{viewModel.businessHoursBinding}" app : items = "@{viewModel.station.businessHours}" app : layoutManager = "@{LayoutManagers.linear()}" />



Last but not least, don’t forget to initialise the data binding in your Activity!



DataBinding in an Activity class DetailActivity : Activity { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val station = intent.getSerializableExtra("station") as GasStation setContentView<DetailActivityBinding>(this, R.layout.detail_activity).viewModel = DetailViewModel(station) } } 1 2 3 4 5 6 7 8 9 class DetailActivity : Activity { override fun onCreate ( savedInstanceState : Bundle ? ) { super . onCreate ( savedInstanceState ) val station = intent . getSerializableExtra ( "station" ) as GasStation setContentView <DetailActivityBinding> ( this , R . layout . detail_activity ) . viewModel = DetailViewModel ( station ) } }

Outcome

All RecyclerViews (prices, opening hours etc.) were concisely implemented for our gas price station app SprIT due to this awesome library.

Conclusion

In our point of view, data binding for RecyclerViews is more intuitive, easier to comprehend, easier to learn and much easier to implement than the ViewHolder-Adapter-RecyclerView way. If you need to implement more complex scenarios, checkout the library’s documentation: https://github.com/evant/binding-collection-adapter.

Stefan