• Android Data Binding: RecyclerView
  • George Mount
  • The Nuggets translation Project
  • Translator: Jamweak
  • Proofreader: Zhiwei Yu, Tanglie

Simplify, reuse, rebind

Sometimes I think that the term “data binding” doesn’t necessarily refer specifically to data binding in Android. RecyclerView has its own unique way of binding its data to UI controls. It has an Adapter that requires us to implement two very important methods for data binding:

RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType);

void onBindViewHolder(RecyclerView.ViewHolder holder, int position);
Copy the code

RecyclerView exposes the common ViewHolder pattern as the first class citizen in its API. In the onCreateViewHolder() method, after the View is created, its reference is included in the ViewHolder so that the data can be quickly configured. Then, in the onBindView() method, that particular data is associated with the View.

Reuse ViewHolder

If you’ve used the ViewHolder of RecyclerView before, you’ll know that we’ve cut out a lot of code for setting data into the View. Unfortunately, we still have to write a lot of ViewHolder for different recyclerViews. Also, if you have multiple View types, it’s not clear how to extend it. We can fix this problem.

Typically, only one data is passed into the binding class, such as “Item” above. When you use this mode, you can use type conversion to use a unique ViewHolder for each RecyclerView and for each View type. The convention is to name a single viewmodel object “obj”. You might call it “item” or “data”, but if I use “obj”, it will be easy to tell in the example.

public class MyViewHolder extends RecyclerView.ViewHolder { private final ViewDataBinding binding; public MyViewHolder(ViewDataBinding binding) { super(binding.getRoot()); this.binding = binding; } public void bind(Object obj) { binding.setVariable(BR.obj, obj); binding.executePendingBindings(); }}Copy the code

In MyViewHolder, I use ViewDataBinding, which is the base class for all generated binding classes in place of the specific ItemBinding class. After doing this, I was able to support a variety of layouts in the ViewHolder. I also used the setVariable() method instead of type-safe, but needed to specify a specific type of setObj() method, so I could specify whatever data type I wanted. The key point is that the variable must be named “obj” because I’m using br.obj as the key for setVariable(). This means you must have a variable tag in your layout file like this:

<variable name="obj" type="Item"/>
Copy the code

Of course, your variables, rather than “Item”, can use any type you want to bind in the layout

Then I was able to create a universal RecyclerView adapter.

public abstract class MyBaseAdapter
                extends RecyclerView.Adapter<MyViewHolder> {
    public MyViewHolder onCreateViewHolder(ViewGroup parent,
                                           int viewType) {
        LayoutInflater layoutInflater =
                LayoutInflater.from(parent.getContext());
        ViewDataBinding binding = DataBindingUtil.inflate(
                layoutInflater, viewType, parent, false);
        return new MyViewHolder(binding);
    }

    public void onBindViewHolder(MyViewHolder holder,
                                 int position) {
        Object obj = getObjForPosition(position);
        holder.bind(obj);
    }

    @Override
    public int getItemViewType(int position) {
        return getLayoutIdForPosition(position);
    }

    protected abstract Object getObjForPosition(int position);

    protected abstract int getLayoutIdForPosition(int position);
}
Copy the code

In this adapter, the layout ID is used as the View type, which makes it easier to get the correct binding class, and also allows the adapter to handle any number of layouts. But the most common way to do this is to RecyclerView only has one layout, so we can write a base class like this:

public abstract class SingleLayoutAdapter extends MyBaseAdapter { private final int layoutId; public SingleLayoutAdapter(int layoutId) { this.layoutId = layoutId; } @Override protected int getLayoutIdForPosition(int position) { return layoutId; }}Copy the code