A simple adapter for a complex RecyclerView

As you know, list is common component for most apps like Facebook, Instagram feeds… For Android developers, the most efficient way to construct a list is using RecyclerView, a widget handles how the views representing items in form of list. RecyclerView (a evolved version of ListView) uses an adapter that tells which element from the collection is going to be shown on the screen, which view is going to use and which views can be recycled.

The ideas behind RecyclerView are simple and effective, but there’s one problem: you need to write an adapter for every collection of items you want to show, and a ViewHolder for every type of item your list can show.

If you’re trying to do something more complex like multiple view types, the process of writing adapters and ViewHolders is going to be very repetitive. You need to implement methods like getItemCount() returning the number of elements in your collection. Isn’t it obvious that it should be the size of the collection? The only interesting thing is the way one item is bound to a ViewHolder. It is actually boilerplate code when you probably only sets some strings into TextViews, uses URLs to load images into ImageViews and so on… Exactly what’s Data Binding is made for in the layout XML.

So why end up writing a whole new adapter every time you want to show a collection of items? Wouldn’t it be cool to simply specify the list of elements you want to show, which item type will be represented by which layout and then use Data Binding to bind every item to its view? That’s what we think and that’s why we’ve made a library that helps us not have to write adapters and ViewHolders over and over again.

We developers at Tiki want to introduce you NoAdapter. And we’d like to show you how helpful it is:

Here’s what you would do:

  1. Enable data binding by adding this into build.gradle
dataBinding {
enabled = true
}

2. Create the layout file for the item item_sample.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable
name="onClick"
type="android.view.View.OnClickListener"/>

<variable
name="item"
type="vn.tiki.noadapter.sample.entity.User"/>

</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
  <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.name}"
android:onClick="@{onClick}"
android:id="@+id:/tvName"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.email}"
android:onClick="@{onClick}"
android:id="@+id:/tvName"/>
</LinearLayout>
</layout>

3. Create an adapter for your fragments or activities:

OnlyAdapter adapter = new OnlyAdapter.Builder()
.layoutSelector(new LayoutSelector() {
@Override public int layoutForType(int type) {
return R.layout.item;
}
}).build();
recyclerView.setAdapter(adapter);

4. Now! You’re done.

When you reaches here, you’ll wonder how do I handler actions like click on a view. As I showed you in step 2, you already saw

<TextView
....
android:onClick="@{onClick}"
android:id="@+id:/tvName"/>
<TextView
...
android:onClick="@{onClick}"
android:id="@+id:/tvName"/>

Then, you need to add a listener for the adapter

adapter = new OnlyAdapter.Builder()
...
.onItemClickListener(new OnItemClickListener() {
@Override public void onItemClick(View view, Object item, int position) {
switch(view.getId()) {
case R.id.tvName:
Log.d(TAG, "Name is clicked");
break;
case R.id.tvEmail:
Log.d(TAG, "Email is clicked");
break;
}
}
}).build();

Callbacks & Listeners

Let’s continue with more features. Right now, you can use Data Binding to set click listeners right on the layout XML and call a method in the item class to handle it. However, if you don’t want to do it that way or there’s any operation that cannot be done through Data Binding, we reserved a callback for you:

new OnlyAdapter.Builder()
...
.customBinding(new ExtraBinding() {
@Override public void onBind(ViewDataBinding binding, Object item, int position) {
if(binding instance of SimpleItemBinding){
//do whatever you want
//tracking here
}
}
})

NoAdapter simplifies the situations where you have multiple item types

It gets harder when you need to have different kind of items in your views. You might even have a list of completely different kind of objects.

In those use cases you have probably different view types you need to show. Means you need to create different ViewHolders and probably inflate different layouts in each. The API defines type identifier as integers, that’s where the ugliness starts! No worries! OnlyAdapter comes to the rescue.

adapter = new OnlyAdapter.Builder()
.layoutSelector(new LayoutSelector() {
@Override public int layoutForType(int type) {
switch(type): {
case 1:
return R.layout.item_dog;
case 2:
return R.layout.item_cat;
case 3:
return R.layout.item_mouse;
case 4:
return R.layout.item_loading_footer;
default:
return R.layout.item_empty;
        }
}
})
.typeDeterminer(new TypeDeterminer() {
@Override public int typeOf(Object item) {
if(item instanceof Dog){
return 1;
} else if (item instanceof Cat ){
return 2;
} else if(item instanceof Mouse){
return 3;
} else if(item instanceof LoadingFooter){
return 4;
}
return 0;
}
})

Advanced usage

In case you do CRUD on your RecyclerView. OnlyAdapter goes hand in hand with AdapterCommand. We do have a sample for you.

In case you have multiple view types and complex views like ViewPager, or HorizontalRecyclerView in a RecyclerView. OnlyAdapter is very handy in such cases. We’ll provide sample project for those cases.

Conclusion

There’s no need to waste your time on writing adapters, ViewHolders and manage complex RecyclerView layouts.