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
- Remove the butter knife, and do not need to use findViewById , the rest of the code to instantiate the control;
- Differentiate between normal list mode and header layout mode , and add a separate header layout;
- 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;
- 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;
}
}