SoFunction
Updated on 2025-03-02

Android RecyclerView Data Binding Instance Code

Preface

There were a lot of RecyclerViews in the previous project, and then I needed to write a lot of Adapters and Viewholders - it would be fine, but there were a lot of duplicate codes in it, so I couldn't bear it! Every Adapter and ViewHolder actually do very similar things: view binding, data binding, click event distribution. What else? Since they do the same thing, why do we still keep writing duplicate codes stupidly?

text

BaseAdapter

How do we usually create one?

  1. Receive a list of data
  2. Rewrite the getItemCount() method to determine the number of Items
  3. Rewrite the onCreateViewHolder() method, bind Layout, and create a new one we wrote ourselves
  4. Rewrite the onBindViewHolder() method to bind data and view
  5. Since RecyclerView does not write a click event, the click event will be distributed

Basically this routine, or add a refreshData() method - pass new data in and notifyDataSetChanged(). Based on these points, I wrote a BaseAdapter base class:


/**
  * Adapter base class.
  * Suitable for RecyclerView with only a single Item.
  *
  * Created by lypeer on 16-5-24.
  */
public abstract class BaseAdapter<V> extends <> {

 /**
   * List of values ​​for each Item loaded
   */
 private List<V> mValueList;

 /**
   * An interface I wrote to distribute click events through callbacks
   */
 private OnItemClickListener<V> mOnItemClickListener;

 @Override
 public  onCreateViewHolder(ViewGroup parent, int viewType) {
  return createViewHolder((), parent);
 }

 @Override
 @SuppressWarnings("unchecked")//It will definitely be a subclass of BaseViewHolder, because the return value of createViewHolder() public void onBindViewHolder( holder, int position) {
  //BaseViewHolder is the base class I abstracted, and there will be a detailed explanation below  ((BaseViewHolder) holder).setData((position), position, mOnItemClickListener);
 }

 /**
   * Set the click event for each Item
   * @param listener
   */
 public void setOnClickListener(OnItemClickListener<V> listener) {
   = listener;
 }

 /**
   * Refresh data
   * @param valueList New data list
   */
 public void refreshData(List<V> valueList) {
   = valueList;
  notifyDataSetChanged();
 }

 @Override
 public int getItemCount() {
  return mValueList == null ? 0 : ();
 }

 /**
   * Generate ViewHolder
   * @param context
   * @param parent
   * @return
   */
 protected abstract BaseViewHolder createViewHolder(Context context, ViewGroup parent);
}

Its subclass needs to specify the specific type of the generic when inheriting it, because different Items may have different data types, so that more Items can be adapted to. In addition, an interface onItemClickListener is mentioned, which is very simple:

/**
  * Click event interface
  * Created by lypeer on 16-5-24.
  */
public interface OnItemClickListener<V> {

 /**
   * Event distribution is performed when the item is clicked
   *
   * @param itemValue The value passed by the clicked item
   * @param viewID Click the control's id
   * @param position The location of the item being clicked
   */
 void onItemClick(V itemValue, int viewID, int position);
}

Generics are also required when using it - the same reason as above.

Through the above BaseAdapter, we encapsulate many common operations in the base class, and its subclass only needs to create new ViewHolders as needed - of course, this viewHolder must be inherited from BaseViewHolder, and there will be a detailed explanation below what BaseViewHolder is. Next is an example. Suppose we now have a RecyclerView in an interface, and the data of each Item is a String value. So how can we use our BaseAdapter to simplify the development process?

public class SampleAdapter extends BaseAdapter<String> {
 @Override
 protected BaseViewHolder createViewHolder(Context context, ViewGroup parent) {
  //SampleViewHolder inherits from BaseViewHolder  return new SampleViewHolder(context, parent);
 }
}

Yes, you read that right! There are only a few lines of code! Complete in 5 seconds! Surprise? !

You just need to create a new SampleAdapter inherited from BaseAdapter, then specify its generic as String, and then return new SampleViewHolder(context, parent) to complete the entire operation.

However, some readers may have doubts: Maybe we need to do a lot of operations in SampleViewHolder? Isn't that just converting the code into a place, and there is actually no optimization.

However, it is not.

BaseViewHolder

I always think it is quite unwise to write ViewHolder in Adapter. In this case, the Adapter class is too heavy and it does too many things. From data reception to interface binding, from control initialization to click event distribution, it is simply done. And this is not good. It's easy to easily have the Adapter code for a more complex RecyclerView that can reach hundreds or thousands of lines, which is terrible, which means that this class will become redundant and difficult to maintain. So how to avoid this? I chose to separate ViewHolders while increasing the responsibilities of ViewHolders so that they can be relatively balanced.

Look directly at the BaseViewHolder code:

/**
  * ViewHolder base class
  *
  * Created by lypeer on 16-5-27.
  */
public abstract class BaseViewHolder<V> extends  {

 public BaseViewHolder(Context context, ViewGroup root, int layoutRes) {
  super((context).inflate(layoutRes, root, false));
  //ButterKnife is used here to bind the control  (this, itemView);
 }

 /**
   * Convenient to its subclasses to perform some operations that require Context.
   *
   * @return The caller's Context
   */
 public Context getContext() {
  return ();
 }

 /**
   * Abstract method, bind data.
   * Let the subclass bind the data and view by itself
   *
   * @param itemValue Item data
   * @param position The position of the current item
   * @param listener Click event listener
   */
 protected abstract void bindData(V itemValue, int position, OnItemClickListener listener);

 /**
   * Used to pass data and information
   *
   * @param itemValue
   * @param position
   * @param listener
   */
 public void setData(V itemValue, int position, OnItemClickListener listener) {
  bindData(itemValue, position, listener);
 }
}

BaseViewHolder also uses generics to suit different data types. At the same time, I used ButterKnife in BaseViewHolder to simplify the code, and I no longer need to findViewById repeatedly.

In addition, you can see that three parameters are passed in the construction method in BaseViewHolder, but in the example above, we only passed in the first two constructor parameters of SampleViewHolder, while the third parameter layoutRes is not passed in. What's going on? Is it written on it wrong? Of course not.There are three parameters in the BaseViewHolder construction method because it requires three parameters, while its subclass only has two parameters because it can only have two parameters. _BaseViewHolder must satisfy its super construct, so it must have those three parameters. If those three parameters are all passed in from the outside world, how can it perform operations on the specialization of that layout?It must explicitly specify the Layout ID_ in the class - this is actually a place I really want to optimize, because in this case, the subclass inherits BaseViewHolder and then needs to modify its construction method. This is quite worry-free, but I haven't thought of any good ideas to optimize it gracefully.

There are two methods that look very similar in BaseViewHolder: setData() and bindData(). However, in fact, except for the same arguments, they are completely different in other aspects. The setData() method is a public method, which can be called by the object of the subclass of BaseViewHolder. Its function is to pass data from the outside for the initialization of the view held by ViewHolder. It can be said to be a bridge for transmitting information; and getData() is an abstract method, and its concrete implementation is in each BaseViewHolder subclass. These subclasses bind and initialize the control in this method, as well as handle the control click events, etc.

Speaking of click event processing, how should its subclasses accomplish this? By OnItemClickListener. We can use the OnItemClickListener interface callback to pass the click events that need to be processed to the outside world, and then handle it by the outside world. So what if there are multiple control click events that need to be handled? Don't worry, because in the onClick method of OnItemClickListener you need to pass in the id of the click control, so that you can judge the incoming id in the outside world, so that different click events are executed for different ids.

Next, let’s take an example to see how to use it, which is the SampleViewHolder above:

public class SampleViewHolder extends BaseViewHolder<String> {

 //A normal clickable TextView @Bind(.is_tv_content)
 TextView mIsTvContent;

 public SampleViewHolder(Context context, ViewGroup root) {
  //The constructor method has been modified, and the Layout ID is explicitly specified here  super(context, root, .item_sample);
 }

 @Override
 protected void bindData(final String itemValue, final int position, final OnItemClickListener listener) {
  // Complete the initialization of the control here and bind it to the data  if (itemValue != null) {
   (itemValue);
  }
  //If you need a click event, pass it out through listener  (new () {
   @Override
   public void onClick(View v) {
    //If the outside world does not call(),    // listener is null    if(listener == null){
     return;
    }
    // The listener passes this event to the outside world for processing without null    (itemValue , () , position);
   }
  });
 }
}

Also very simple, convenient, fast and clear. But there are still a few points worth noting. First of all, you cannot forget to modify the construction method and explicitly specify the Layout ID in super, otherwise you will not be able to do the next step, and you will be confused. In addition, when calling the() method, the listener's null check must be performed - because the listener is really possible! In this case, it is very easy to generate null exceptions and cause program crashes.

Bingo, just like that, SampleViewHolder is done, just as hard as it can be.

Conclusion

The purpose of this blog post is to share some of the things I have summarized, hoping to accelerate everyone's development a little bit. Of course, there may be bugs that I haven't found. If you find any problems during the use process, please don't be polite and smash them hard on me!

Finally, let’s summarize how to get the fastest set of RecyclerView with BaseAdapter and BaseViewHolder:

ViewHolder Related
Create a new XXXViewHolder inherits from BaseViewHolder and specifies the generic type (that is, the data type of data in the Item).
Delete the layoutRes parameter in the constructor and explicitly specify the Layout ID in super.
Bind controls with ButterKnife.
Complete the initialization of the control and the passing of click events in the bindData() method (don't forget the listener's space check)

Adapter related

New XXXAdapter inherits from BaseAdapter and specifies the generic type (that is, the data type of data in Item).
return new XXXViewHolder(context, parent);

Related from outside

Bind RecyclerView and create a new XXXAdapter.
Call the () method to pass into the data list.
If there is a need for click event processing, the() method is called.

Currently I have only made BaseAdapters for single Items in RecyclerView, but BaseViewHolder makes it universal and can greatly simplify the volume of Adapter under multiple Items.

The above is a compilation of the data binding of Android RecyclerView. We will continue to add relevant information in the future. Thank you for your support for this site!