About the encapsulation of BaseRecyclerViewAdapter in the MVVM framework

About the encapsulation of BaseRecyclerViewAdapter in the MVVM framework

foreword

Due to the incompatibility of the butter knife after Android Studio 4.0+, and the official announcement that it will no longer be adapted, it is recommended that you use the official ViewDataBinding, so the Adapter of RecyclerView is repackaged

plan

  1. Remove the butter knife, and do not need to use findViewById , the rest of the code to instantiate the control;
  2. Differentiate between normal list mode and header layout mode , and add a separate header layout;
  3. Add click event and long press event functions, add touch events to the list separately, pay attention to the special case of header layout mode;
  4. ViewHolder package , unified ViewHolder package;

Encapsulated code effect

First, the encapsulation code effect will be so concise when using Adapter:

/**
 * Created by CherishTang on 2017/10/20.
 * describe:笑话大全
 */
public class JokeAdapter extends BaseRecyclerViewAdapter<JokeBean,JokeItemBinding> {
    
    

    public JokeAdapter(Context context) {
    
    
        super(context);
    }

    @Override
    protected int getLayoutId() {
    
    
        return R.layout.joke_item;
    }

    @Override
    public void onBindHolder(BaseViewHolder<JokeItemBinding> holder, int pos) {
    
    
        holder.getBinding().setItem(mList.get(pos));
    }
}

start packaging

First, we create a BaseRecyclerViewAdapter base class and start coding

/**
 * Created by CherishTang on 2021/5/24 13:36
 * describe:
 */
public class BaseRecyclerViewAdapter extends RecyclerView.Adapter {
    
    
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
    // 返回创建一个ViewHolder
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    
    
    // 这里是刷新Item
    }

    @Override
    public int getItemCount() {
    
    
    // 有多少个Item
        return 0;
    }
}

After creating the base class, the system will allow you to implement these methods by default. At this time, you will think that I need to know the data type that each list needs to be filled in, right? So let's make a little modification, add some generic restrictions and Basic package:

/**
 * Created by CherishTang on 2021/5/24 13:36
 * describe:BaseRecyclerViewAdapter封装,T为实体类泛型,VDB 为常规下itemView的布局
 */
public class BaseRecyclerViewAdapter<T, VDB extends ViewDataBinding> extends RecyclerView.Adapter {
    
    
    protected Context mContext;
    protected List<T> mList = new ArrayList<>();
        public BaseRecyclerViewAdapter(Context context) {
    
    
        mContext = context;
    }

    public BaseRecyclerViewAdapter(Context context, List<T> list) {
    
    
        mContext = context;
        mList = (list == null) ? new ArrayList<T>() : list;
        setList(mList);
    }
    
    protected abstract int getLayoutId();
    public void setList(List<T> list) {
    
    
        this.mList = list;
    }
    
    public void addAll(List<T> items) {
    
    
        if (items != null) {
    
    
            this.mList.addAll(items);
            notifyItemRangeInserted(this.mList.size(), items.size());
        }
    }

    public List<T> getList() {
    
    
        return mList;
    }

    public void setList(T[] list) {
    
    
        ArrayList<T> arrayList = new ArrayList<T>(list == null ? 0 : list.length);
        if (list != null) {
    
    
            arrayList.addAll(Arrays.asList(list));
        }
        setList(arrayList);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
    // 返回创建一个ViewHolder
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    
    
    // 这里是刷新Item
    }

    @Override
    public int getItemCount() {
    
    
    // 有多少个Item
        return 0;
    }
}

Of course, if you want to set the header layout separately, you still need this method. For the time being, we will not do such a complicated list display method. You can expand it according to this article.

    /**
     * 头布局类型
     */ 
    public static final int TYPE_HEAD = 1;
    /**
     * 常用的列表类型
     */ 
    public static final int TYPE_NORMAL = 0;
    
    @Override
    public int getItemViewType(int position) {
    
    
        if (headerView == null) {
    
    
            return TYPE_NORMAL;
        }
        if (position == 0) {
    
    
            return TYPE_HEAD;
        }
        return TYPE_NORMAL;
    }

Next, we add the method of setting the header layout for the Adapter

    private int headerViewId;
    private View headerView;
    public int getHeaderViewId() {
    
    
        return headerViewId;
    }
    
    public void setHeaderView(@LayoutRes int headerViewRes) {
    
    
        this.headerViewId = headerViewRes;
        headerView = LayoutInflater.from(mContext).inflate(headerViewRes, null);
        this.headerView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
                RecyclerView.LayoutParams.WRAP_CONTENT));
        notifyItemInserted(0);
    }

    public void setHeaderView(View headerView) {
    
    
        this.headerView = headerView;
        notifyItemInserted(0);
    }

Then we create our own item layout for each of our items

   @NotNull
    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
    
            return new BaseViewHolder<>(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), getLayoutId(), parent, false));
    }

But if you write like this, how is the header layout instantiated? If we need to add the judgment of the header layout to perform a separate instantiation of the header layout and create a ViewHolder belonging to it

    private int headerViewId;
    private View headerView;
    
    public View getHeaderView() {
    
    
        return headerView;
    }
    
    @NotNull
    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
    
        if (viewType == TYPE_HEAD && getHeaderView() != null) {
    
    
            return createHeaderViewHold(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), getHeaderViewId(), parent, false));
        } else {
    
    
            return createViewHold(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), getLayoutId(), parent, false));
        }
    }

    /**
     * 获取自定义viewHolder对象,如果不自定义借直接返回baseViewHolder
     *
     * @param binding item布局
     * @return BaseViewHolder
     */
    protected <HVDB extends ViewDataBinding> BaseViewHolder<HVDB> createHeaderViewHold(HVDB binding) {
    
    
        return new BaseViewHolder<>(binding,true);
    }
    /**
     * 获取自定义viewHolder对象,如果不自定义借直接返回baseViewHolder
     *
     * @param binding item布局
     * @return BaseViewHolder
     */
    protected BaseViewHolder<VDB> createViewHold(VDB binding) {
    
    
        return new BaseViewHolder<>(binding);
    }

Our BaseViewHolder code:

/**
 * Created by CherishTang on 2017/12/28.
 * describe:ViewHolder封装
 */
public class BaseViewHolder<VDB extends ViewDataBinding> extends RecyclerView.ViewHolder {
    
    

    private VDB binding;
    private ViewDataBinding headerBinding;
    private boolean isHeaderViewHolder = false;

    public BaseViewHolder(VDB binding) {
    
    
        super(binding.getRoot());
        this.binding = binding;
    }

    public BaseViewHolder(ViewDataBinding headerBinding, boolean isHeaderViewHolder) {
    
    
        super(headerBinding.getRoot());
        this.isHeaderViewHolder = isHeaderViewHolder;
        this.headerBinding = headerBinding;
    }

    public VDB getBinding() {
    
    
        return binding;
    }

    public ViewDataBinding getHeaderBinding() {
    
    
        return headerBinding;
    }
}

Now we only need to add a click event to each itemView, and the encapsulation is basically done. First, we create a new interface for click and long press events

    public OnItemClickListener mOnItemClickListener;
    public OnItemLongClickListener mOnItemLongClickListener;
    public void setOnItemClickListener(OnItemClickListener l) {
    
    
        mOnItemClickListener = l;
    }

    public void setOnItemLongClickListener(OnItemLongClickListener l) {
    
    
        mOnItemLongClickListener = l;
    }

    public interface OnItemClickListener {
    
    
        void onItemClick(View view, int position);
    }

    public interface OnItemLongClickListener {
    
    
        void onItemLongClick(View view, int position);
    }

Then we use it in the onBindViewHolder method

    @Override
    public void onBindViewHolder(final BaseViewHolder baseViewHolder, final int pos) {
    
    
    	//判断当前item为头布局的类型的话,我们就执行头布局的方法
        if (getItemViewType(pos) == TYPE_HEAD) {
    
    
            if (baseViewHolder == null) {
    
    
                return;
            }
            //自定义方法,方便继承方法去实现头布局的具体操作
            onBindHeaderHolder(baseViewHolder, getRealPosition(baseViewHolder));
            return;
        }
        baseViewHolder.getBinding().getRoot().setOnClickListener(v -> {
    
    
            if (mOnItemClickListener != null) {
    
    
                mOnItemClickListener.onItemClick(baseViewHolder.getBinding().getRoot(), getRealPosition(baseViewHolder));
            }
        });
        baseViewHolder.getBinding().getRoot().setOnLongClickListener(v -> {
    
    
            if (mOnItemLongClickListener != null) {
    
    
                mOnItemLongClickListener.onItemLongClick(baseViewHolder.getBinding().getRoot(), getRealPosition(baseViewHolder));
                return true;
            }
            return false;
        });
        //暴露一个抽象方法,在此方法中去实现列表的复制等操作
        onBindHolder(baseViewHolder, getRealPosition(baseViewHolder));
    }

    /**
     * 设置数据
     *
     * @param holder
     * @param pos
     */
    public void onBindHeaderHolder(final BaseViewHolder holder, final int pos) {
    
    

    }

    /**
     * 设置数据
     *
     * @param holder
     * @param pos
     */
    public abstract void onBindHolder(final BaseViewHolder<VDB> holder, final int pos);

    public int getRealPosition(RecyclerView.ViewHolder holder) {
    
    
        int position = holder.getLayoutPosition();
        return headerView == null ? position : position - 1;
    }

Well, the encapsulation is basically ready to use here, the complete code is below

package com.jcloudsoft.demo.base;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.LayoutRes;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.recyclerview.widget.RecyclerView;

import com.jcloudsoft.demo.wight.recyclerview.SimpleItemTouchHelperCallback;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Created by CherishTang on 2017/12/28.
 * BaseRecyclerViewAdapter封装
 */
public abstract class BaseRecyclerViewAdapter<T, VDB extends ViewDataBinding> extends RecyclerView.Adapter<BaseViewHolder<VDB>> implements
        SimpleItemTouchHelperCallback.ItemTouchHelperAdapter {
    
    
    protected Context mContext;
    protected List<T> mList = new ArrayList<>();
    protected RecyclerView mRecyclerView;
    private int headerViewId;
    private View headerView;
    public static final int TYPE_HEAD = 1;
    public static final int TYPE_NORMAL = 0;

    public OnItemClickListener mOnItemClickListener;
    public OnItemLongClickListener mOnItemLongClickListener;

    public BaseRecyclerViewAdapter(Context context) {
    
    
        mContext = context;
    }

    public BaseRecyclerViewAdapter(Context context, List<T> list) {
    
    
        mContext = context;
        mList = (list == null) ? new ArrayList<T>() : list;
        setList(mList);
    }

    public void setHeaderView(@LayoutRes int headerViewRes) {
    
    
        this.headerViewId = headerViewRes;
        headerView = LayoutInflater.from(mContext).inflate(headerViewRes, null);
        this.headerView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
                RecyclerView.LayoutParams.WRAP_CONTENT));
        notifyItemInserted(0);
    }

    public void setHeaderView(View headerView) {
    
    
        this.headerView = headerView;
        notifyItemInserted(0);
    }

    public void addAll(List<T> items) {
    
    
        if (items != null) {
    
    
            this.mList.addAll(items);
            notifyItemRangeInserted(this.mList.size(), items.size());
        }
    }

    public int getHeaderViewId() {
    
    
        return headerViewId;
    }

    public View getHeaderView() {
    
    
        return headerView;
    }

    @Override
    public void onBindViewHolder(final BaseViewHolder baseViewHolder, final int pos) {
    
    
        if (getItemViewType(pos) == TYPE_HEAD) {
    
    
            if (baseViewHolder == null) {
    
    
                return;
            }
            onBindHeaderHolder(baseViewHolder, getRealPosition(baseViewHolder));
            return;
        }
        baseViewHolder.getBinding().getRoot().setOnClickListener(v -> {
    
    
            if (mOnItemClickListener != null) {
    
    
                mOnItemClickListener.onItemClick(baseViewHolder.getBinding().getRoot(), getRealPosition(baseViewHolder));
            }
        });
        baseViewHolder.getBinding().getRoot().setOnLongClickListener(v -> {
    
    
            if (mOnItemLongClickListener != null) {
    
    
                mOnItemLongClickListener.onItemLongClick(baseViewHolder.getBinding().getRoot(), getRealPosition(baseViewHolder));
                return true;
            }
            return false;
        });
        onBindHolder(baseViewHolder, getRealPosition(baseViewHolder));
    }

    /**
     * 设置数据
     *
     * @param holder
     * @param pos
     */
    public void onBindHeaderHolder(final BaseViewHolder holder, final int pos) {
    
    

    }

    /**
     * 设置数据
     *
     * @param holder
     * @param pos
     */
    public abstract void onBindHolder(final BaseViewHolder<VDB> holder, final int pos);

    @Override
    public int getItemCount() {
    
    
        int count = (mList == null ? 0 : mList.size());
        if (headerView != null) {
    
    
            count++;
        }
        return count;
    }

    @Override
    public int getItemViewType(int position) {
    
    
        if (headerView == null) {
    
    
            return TYPE_NORMAL;
        }
        if (position == 0) {
    
    
            return TYPE_HEAD;
        }
        return TYPE_NORMAL;
    }

    @NotNull
    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
    
        if (viewType == TYPE_HEAD && getHeaderView() != null) {
    
    
            return createHeaderViewHold(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), getHeaderViewId(), parent, false));
        } else {
    
    
            return createViewHold(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), getLayoutId(), parent, false));
        }
    }

    /**
     * 获取自定义viewHolder对象,如果不自定义借直接返回baseViewHolder
     *
     * @param binding item布局
     * @return BaseViewHolder
     */
    protected <HVDB extends ViewDataBinding> BaseViewHolder<HVDB> createHeaderViewHold(HVDB binding) {
    
    
        return new BaseViewHolder<>(binding,true);
    }
    /**
     * 获取自定义viewHolder对象,如果不自定义借直接返回baseViewHolder
     *
     * @param binding item布局
     * @return BaseViewHolder
     */
    protected BaseViewHolder<VDB> createViewHold(VDB binding) {
    
    
        return new BaseViewHolder<>(binding);
    }

    protected abstract int getLayoutId();

    public int getItemViewHeight() {
    
    
        return 0;
    }

    public void setList(List<T> list) {
    
    
        this.mList = list;
    }

    public int getRealPosition(RecyclerView.ViewHolder holder) {
    
    
        int position = holder.getLayoutPosition();
        return headerView == null ? position : position - 1;
    }

    public List<T> getList() {
    
    
        return mList;
    }

    public void setList(T[] list) {
    
    
        ArrayList<T> arrayList = new ArrayList<T>(list == null ? 0 : list.length);
        if (list != null) {
    
    
            arrayList.addAll(Arrays.asList(list));
        }
        setList(arrayList);
    }

    @Override
    public void onItemDismiss(int position) {
    
    
        mList.remove(position);
        notifyItemRemoved(position);
    }

    @Override
    public void onItemMove(int from, int to) {
    
    
        Collections.swap(mList, from, to);
        notifyItemMoved(from, to);
    }

    public RecyclerView getRecyclerView() {
    
    
        return mRecyclerView;
    }

    public void setRecyclerView(RecyclerView recyclerView) {
    
    
        mRecyclerView = recyclerView;
    }

    public void setOnItemClickListener(OnItemClickListener l) {
    
    
        mOnItemClickListener = l;
    }

    public void setOnItemLongClickListener(OnItemLongClickListener l) {
    
    
        mOnItemLongClickListener = l;
    }

    public interface OnItemClickListener {
    
    
        void onItemClick(View view, int position);
    }

    public interface OnItemLongClickListener {
    
    
        void onItemLongClick(View view, int position);
    }
}

BaseViewHolder:

package com.jcloudsoft.demo.base;


import androidx.databinding.ViewDataBinding;
import androidx.recyclerview.widget.RecyclerView;

/**
 * Created by CherishTang on 2017/12/28.
 */

public class BaseViewHolder<VDB extends ViewDataBinding> extends RecyclerView.ViewHolder {
    
    

    private VDB binding;
    private ViewDataBinding headerBinding;
    private boolean isHeaderViewHolder = false;

    public BaseViewHolder(VDB binding) {
    
    
        super(binding.getRoot());
        this.binding = binding;
    }

    public BaseViewHolder(ViewDataBinding headerBinding, boolean isHeaderViewHolder) {
    
    
        super(headerBinding.getRoot());
        this.isHeaderViewHolder = isHeaderViewHolder;
        this.headerBinding = headerBinding;
    }

    public VDB getBinding() {
    
    
        return binding;
    }

    public ViewDataBinding getHeaderBinding() {
    
    
        return headerBinding;
    }
}

Guess you like

Origin blog.csdn.net/fzkf9225/article/details/117222489