Analysis of the simplified process of RecyclerView's Adapter

Preface

The previous article introduced a library for the extension and encapsulation of RecyclerView, which helps us to quickly add a list during development and improve development efficiency. Students who have not read this article can move to the previous article RecyclerView to elegantly encapsulate the Adapter after reading this article to get all the lists . This article is about the idea and process of Adapter simplification.

background

In Android development, the list we encounter most is the list. There are simple lists in an APP, and there are also complex lists containing many kinds of items. Most App homepages are more complicated. For example, the homepage of a social APP contains banner area, advertising area, text content, image content, video content, etc. RecyclerView can use ViewType to distinguish different items, which can also meet the needs, but there are still some problems, such as: 1. In the item list interface with too many logic and complexity, the amount of code in the adapter is huge, the logic is complicated, and it is difficult to maintain in the later stage. 2. Every time a list is added, an Adapter needs to be added, and the bricks are moved repeatedly, which is inefficient. So this article is about simplifying our Adapter from these two dimensions.

Idea analysis and realization process

Two issues are raised above, let's take a look at how we can solve these two issues. First of all, let's take a look at our regular writing of a complex list interface containing multiple items (for example, the front page containing banner, advertisement, text content, image content, and video content):

public class HomePageAdapter extends RecyclerView.Adapter {
    
    
    public static final int TYPE_BANNER = 0;
    public static final int TYPE_AD = 1;
public static final int TYPE_TEXT = 2;
    public static final int TYPE_IMAGE = 3;
    public static final int TYPE_VIDEO = 4;
    private List<HomePageEntry> mData;

    public void setData(List<HomePageEntry> data) {
        mData = data;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType){
            case TYPE_BANNER:
                return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_banner_layout,null));
            case TYPE_AD:
                return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_ad_item_layout,null));
            case TYPE_TEXT:
                return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_text_item_layout,null));
            case TYPE_IMAGE:
                return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_image_item_layout,null));
            case TYPE_VIDEO:
                return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_video_item_layout,null));
        }
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int type = getItemViewType(position);
        switch (type){
            case TYPE_BANNER:
                // banner 逻辑处理
                break;
            case TYPE_AD:
                // 广告逻辑处理
                break;
            case TYPE_TEXT:
                // 文本逻辑处理
                break;
            case TYPE_IMAGE:
               //图片逻辑处理
                break;
            case TYPE_VIDEO:
                //视频逻辑处理
                break;
            // ... 此处省去N行代码
        }
    }

    @Override
    public int getItemViewType(int position) {
        if(position == 0){
            return TYPE_BANNER;//banner在开头
        }else {
            return mData.get(position).type;//type 的值为TYPE_AD,TYPE_IMAGE,TYPE_AD,TYPE_VIDEO其中一个
        }

    }

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



    public static class BannerViewHolder extends RecyclerView.ViewHolder{
    
    

        public BannerViewHolder(View itemView) {
            super(itemView);
            //绑定控件
        }
    }

    public static class VideoViewHolder extends RecyclerView.ViewHolder{
    
    

        public VideoViewHolder(View itemView) {
            super(itemView);
            //绑定控件
        }
    }
    public static class AdViewHolder extends RecyclerView.ViewHolder{
    
    

        public AdViewHolder(View itemView) {
            super(itemView);
            //绑定控件
        }
    }
    public static class TextViewHolder extends RecyclerView.ViewHolder{
    
    

        public TextViewHolder(View itemView) {
            super(itemView);
            //绑定控件
        }
    }
    public static class ImageViewHolder extends RecyclerView.ViewHolder{
    
    

        public ImageViewHolder(View itemView) {
            super(itemView);
            //绑定控件
        }
    }
}

The above is the way we usually write a multi-item list, and process different items according to different ViewTypes. If the logic is complicated, the amount of code for this class is very huge. If the version iteratively adds new requirements, it is troublesome to modify the code and difficult to maintain later.

In fact, it can be seen that the amount of code lies in the view binding, data binding, and logical processing of each item, and these operations are common operations of the Item. Since they are common, then we can extract them. Therefore, we can abstract the Item into an independent component (we call this component Cell), which is responsible for the view binding, data binding and logical processing of each item. Now we can put the code in the Adapter into the Cell went. Adapter binds the view and binds the data to call the method of the corresponding cell.

Let's take a look at Cell. As an independent component, Cell has the following functions:
1, view binding
2, data binding
3, resource release
4, independent id (let Adapter distinguish which kind of Cell)

1. First, we define a top-level interface Cell, corresponding to the above 4 functions, there are 4 methods


public interface Cell {
    
    
    /**
     * 回收资源
     *
     */
    public void releaseResource();

    /**
     * 获取viewType
     * @return
     */
    public  int getItemType();

    /**
     * 创建ViewHolder
     * @param parent
     * @param viewType
     * @return
     */
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * 数据绑定
     * @param holder
     * @param position
     */
    public  void onBindViewHolder(RecyclerView.ViewHolder holder, int position);
}

2. Since data can be bound, we need to give it a data source, so we define a base class RVBaseCell to save a paradigm data entity

public  abstract class RVBaseCell<T> implements Cell {
    
    

    public RVBaseCell(T t){
        mData = t;
    }
    public T mData;

    @Override
    public void releaseResource() {
        // do nothing
        // 如果有需要回收的资源,子类自己实现
    }
}

3, in view of the original Adapter binding, binding data, logical processing operations, into the corresponding Cell do
example, we created a Cell is Banner, called BannerCell. code show as below:


public class BannerCell extends RVBaseCell<HomePageEntry>{
    
    
    public static final int TYPE_BANNER = 0;
    public BannerCell(HomePageEntry homePageEntry) {
        super(homePageEntry);
    }

    @Override
    public int getItemType() {
        return TYPE_BANNER;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_banner_layout,null));
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        //处理banner 逻辑
    }

    public static class BannerViewHolder extends RecyclerView.ViewHolder{
    
    

        public BannerViewHolder(View itemView) {
            super(itemView);
            //绑定控件
        }
    }
}

4. Transform the Adapter. What is stored in the Adapter is no longer entity data, but a list of Cells. The callback method of the Adapter is transformed, and the method in the Cell at the corresponding location is called.
code show as below:

public  class HomePageAdapter<C extends RVBaseCell> extends RecyclerView.Adapter {
    
    
    private List<C> mData;

    public void setData(List<C> data) {
        mData = data;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        for(int i=0;i<getItemCount();i++){
            if(viewType == mData.get(i).getItemType()){
                return mData.get(i).onCreateViewHolder(parent,viewType);
            }
        }

        throw new RuntimeException("wrong viewType");
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemViewType(int position) {
        return mData.get(position).getItemType();

    }

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

}

Now take a look at the code of this HomePageAdapter and the code of HomePageAdapter written before, you can reduce an Adapter with thousands of lines of code to only a few dozen lines of code, and more importantly, this Adapter is a universal one. All our lists only It's OK if you need such an Adapter, just add Cell. In this way, the two problems mentioned at the beginning of our article are solved.

We can use two pictures to compare. Before adding a list, we would write an Adapter:

Write picture description here

Now it looks like this:

new_adapter.png

As shown in the figure above, only one Adapter is needed, just like there are many slots on the Adapter, we plug each Cell into the Adapter, plug and play. Completely decoupled, you only need to add or reduce a type of Cell in the future to add requirements and cut down requirements, without moving the old code before, which is easy to maintain.
At last

The above is the simplified packaging process and thinking analysis of RecyclerView Adapter.

Guess you like

Origin blog.csdn.net/hai_jian/article/details/73801088