Android Data Binding for RecyclerView: flexible way
- Tutorial

A lot of time has passed since the first announcement on Google IO 2015 of the new Data Binding Library . There are many examples, many guides and many corrections and tweaks in the library itself. Binding has already become two-way, and you can refer to other View by their id in the layout file itself and the army of fans of this library is growing steadily. And, probably, each new adept begins with a search for examples - how to use it correctly and conveniently, with less code and Feng Shui. If you drive in a request similar to “Android DataBinding + RecyclerView”, then, for sure, we get a whole bunch of links to various guides. Even on Habré there was already a similar article - Android Data Binding in RecyclerView .
But despite such an abundance of resources / guides, many of them show basic functionality, and each developer, starting to actively use Data Binding, comes up with his own, convenient way of working. Next will be shown one of these methods.
Example here: DataBinding_For_RecyclerView
Stages:
- implementation / configuration of the Adapter (viewTypes, items, processing clicks on elements and inside the list elements themselves);
- setting RecyclerView (set LayoutManager, Adapter, ItemDecorator, ItemAnimator, item divider size, ScrollListener, ...).
Configuring RecyclerView
For now, let’s leave the adapter implementation and consider how to configure RecyclerView itself. The easiest way here is to simply assign an id to the RecyclerView and set all parameters in the code:
mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context)); // + другие настройки.
The second, often encountered way is to make some of the most banal initializations in the code, for example, like this:
<android.support.v7.widget.RecyclerViewapp:layoutManager="android.support.v7.widget.GridLayoutManager" />
And finally, the method used by the author is to use the intermediary class, which will be configured in the code and applied through the binding. The profit of this approach is the ability to hide the "default" settings inside the intermediary class (helper), but at the same time have full control over the configuration of RecyclerView in one place.
<android.support.v7.widget.RecyclerViewapp:listConfig="@{viewModel.listConfig}"/>
In code:
ListConfig listConfig = new ListConfig.Builder(mAdapter)
//.setLayoutManagerProvider(new GridLayoutManagerProvider(mCount, mSpanLookup)) //LinearLayoutManager if not set//.addItemDecoration(new ColorDividerItemDecoration(color, spacing, SPACE_LEFT | SPACE_TOP, false))
.setDefaultDividerEnabled(true)
.addOnScrollListener(new OnLoadMoreScrollListener(mCallback))
.setItemAnimator(getItemAnimator())
.setHasFixedSize(true)
.setItemTouchHelper(getItemTouchHelper())
.build(context);
What ListConfig Is
publicclassListConfig{
// Adapter, LayoutManager, ItemAnimator, ItemDecorations, ScrollListeners,// ItemTouchHelper, hasFixedSizeprivateListConfig(/*params*/){
// init fields
}
publicvoidapplyConfig(final Context context, final RecyclerView recyclerView){
//... apply config
}
publicstaticclassBuilder{
publicBuilder(Adapter adapter){/*set field*/}
public Builder setLayoutManagerProvider(LayoutManagerProvider layoutManagerProvider){/*set field*/}
public Builder setItemAnimator(ItemAnimator itemAnimator){/*set field*/}
public Builder addItemDecoration(ItemDecoration itemDecoration){/*set field*/}
public Builder addOnScrollListener(OnScrollListener onScrollListener){/*set field*/}
public Builder setHasFixedSize(boolean isFixedSize){/*set field*/}
public Builder setDefaultDividerEnabled(boolean isEnabled){/*set field*/}
public Builder setDefaultDividerSize(int size){/*set field*/}
public Builder setItemTouchHelper(ItemTouchHelper itemTouchHelper){/*set field*/}
public ListConfig build(Context context){
/*set default values*/returnnew ListConfig(/*params*/);
}
}
publicinterfaceLayoutManagerProvider{
LayoutManager get(Context context);
}
}
Flexible adapter implementation
One of the most interesting questions is inheritance or composition. Many repeat the “Prefer composition over inheritance” mantra, but still continue to produce heirs from heirs from heirs ... Whoever is not familiar with the terrific article on this subject as applied to List Adapters, be sure to check out JOE'S GREAT ADAPTER HELL ESCAPE . In short, let’s imagine this situation: we are given the task to implement a simple application with 2 lists: a list of users (User) and a list of locations (Location). Nothing complicated, right?) We create two adapters, - UserAdapter and LocationAdapter, - and, in fact, that's all. But here, in the next “sprint” (we are Agile, right?), The customer wants to add advertising to each of these lists (Advertisment).
publicclassUserimplementsBaseModel{
public String name;
public String avatar;
}
publicclassLocationimplementsBaseModel{
public String name;
public String image;
}
publicclassAdvertisementimplementsBaseModel{
public String label;
public String image;
}
No problem, we say, we create another AdvertismentAdapter adapter and inherit both of the previous ones: UserAdapter extends AdvertismentAdapter and LocationAdapter extends AdvertismentAdapter. Everything is fine, everyone is happy, but in the new “sprint” the client wants another list where all 3 entities will be mixed at once. What to do now?
And here we go from inheritance to composition. Before that, we had a separate adapter for each list with its own types (viewTypes), now we will replace this system with one adapter and 3 delegates for each type of list item. The adapter will not know anything about the types of elements that it displays, but knows that it has several delegates, asking each one in turn, you can find the specific one for the desired list item and delegate the creation of this element to it.
Delegate adapter

In this case, it does not matter to us how many lists and with what types of elements they will be, any list is formed as a constructor - by a set of delegates.
mAdapter = new DelegateAdapter<>(
new UserDelegate(actionHandler),
// or new ModelItemDelegate(User.class, R.layout.item_user, BR.user),new LocationDelegate(),
new AdvertismentDelegate(),
// ...
);
Example delegate implementation (UserDelegate)
publicclassUserDelegateextendsActionAdapterDelegate<BaseModel, ItemUserBinding> {
publicUserDelegate(final ActionClickListener actionHandler){
super(actionHandler);
}
@OverridepublicbooleanisForViewType(@NonNull final List<BaseModel> items, finalint position){
return items.get(position) instanceof User;
}
@NonNull@Overridepublic BindingHolder<ItemUserBinding> onCreateViewHolder(final ViewGroup parent){
return BindingHolder.newInstance(R.layout.item_user, LayoutInflater.from(parent.getContext()), parent, false);
}
@OverridepublicvoidonBindViewHolder(@NonNull final List<BaseModel> items, finalint position, @NonNull final BindingHolder<ItemUserBinding> holder){
final User user = (User) items.get(position);
holder.getBinding().setUser(user);
holder.getBinding().setActionHandler(getActionHandler());
}
@OverridepubliclonggetItemId(final List<BaseModel> items, finalint position){
return items.get(position).getId();
}
}
As for DataBinding, all the magic is in a special ViewHolder:
publicclassBindingHolder<VBextendsViewDataBinding> extendsRecyclerView.ViewHolder{
private VB mBinding;
publicstatic <VB extends ViewDataBinding> BindingHolder<VB> newInstance(
@LayoutRes int layoutId, LayoutInflater inflater, ViewGroup parent, boolean attachToParent){
final VB vb = DataBindingUtil.inflate(inflater, layoutId, parent, attachToParent);
returnnew BindingHolder<>(vb);
}
publicBindingHolder(VB binding){
super(binding.getRoot());
mBinding = binding;
}
public VB getBinding(){
return mBinding;
}
}
If, however, it is too lazy to create a separate delegate for each new type / type of list item, you can use the binding feature and use a single universal delegate for any type:
// new UserDelegate(actionHandler),new ModelItemDelegate(User.class, R.layout.item_user);
// ornew ModelItemDelegate(R.layout.item_user, BR.model, (item) -> item instance of User);
ModelItemDelegate
publicclassModelItemDelegate<T> extendsBaseListBindingAdapterDelegate<T, ViewDataBinding> {
privatefinalint mModelId;
privatefinalint mItemLayoutResId;
privatefinal ViewTypeClause mViewTypeClause;
publicModelItemDelegate(@NonNull Class<? extends T> modelClass, @LayoutRes int itemLayoutResId){
this(itemLayoutResId, BR.model, new SimpleViewTypeClause(modelClass));
}
publicModelItemDelegate(@LayoutRes int itemLayoutResId, int modelId, ViewTypeClause viewTypeClause){
mItemLayoutResId = itemLayoutResId;
mViewTypeClause = viewTypeClause;
mModelId = modelId != 0 ? modelId : BR.model;
}
@OverridepublicbooleanisForViewType(@NonNull List<T> items, int position){
return mViewTypeClause.isForViewType(items, position);
}
@NonNull@Overridepublic BindingHolder<ViewDataBinding> onCreateViewHolder(ViewGroup parent){
return BindingHolder.newInstance(mItemLayoutResId, LayoutInflater.from(parent.getContext()), parent, false);
}
@OverridepublicvoidonBindViewHolder(@NonNull List<T> items, int position, @NonNull BindingHolder<ViewDataBinding> holder){
ViewDataBinding binding = holder.getBinding();
binding.setVariable(mModelId, items.get(position));
binding.executePendingBindings();
}
publicinterfaceViewTypeClause{
booleanisForViewType(List<?> items, int position);
}
publicstaticclassSimpleViewTypeClauseimplementsViewTypeClause{
privatefinal Class<?> mClass;
publicSimpleViewTypeClause(@NonNull Class<?> aClass){
mClass = aClass;
}
@OverridepublicbooleanisForViewType(List<?> items, int position){
return mClass.isAssignableFrom(items.get(position).getClass());
}
}
}
Processing clicks on elements is easy to implement by passing a click handler through binding, for example, as described here - Android and Data Binding: processing actions , or using any other method convenient for you.
Conclusion
Thus, using the Android Data Binding Library, the implementation of lists becomes a completely ordinary thing. You don’t even need to write an implementation of the things shown above, but simply by importing the author’s finished library, or simply “copying” them from there.
Library with an example: DataBinding_For_RecyclerView