前言
由于在Android Studio4.0+后黄油刀的不适配,而且官方也出公告了不会再适配了,推荐大家使用官方的ViewDataBinding,所以重新对RecyclerView的Adapter重新进行了封装
计划
- 去除黄油刀,并且不需要使用findViewById ,剩余实例化控件的代码;
- 区别出普通列表模式和头布局模式 ,可以新增单独的头布局;
- 增加点击事件、长按事件 功能,给列表单独添加触控事件,注意有头布局模式的特殊情况;
- ViewHolder封装, 统一的ViewHolder封装;
封装后代码效果
先上个封装后的代码效果,当使用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));
}
}
开始封装
首先我们新建一个BaseRecyclerViewAdapter基类,开始撸码
/**
* 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;
}
}
新建完基类后,系统会默认让你实现这几个方法,这个时候你会想到我需要知道每个列表需要填充的数据类型对吧,所以我们稍加改造下,加上一些泛型限制和基本封装:
/**
* 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;
}
}
当然如果你想实现单独设置头布局的话你还需要这个方法,暂时我们不做那么复杂的列表展现方式,大家可以根据这篇文章后进行拓展
/**
* 头布局类型
*/
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;
}
接下来就是我们加上给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);
}
然后就是针对我们每一个item创建属于自己的item布局了
@NotNull
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new BaseViewHolder<>(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), getLayoutId(), parent, false));
}
但是如果你这么写的话,那头布局如何实例化呢?如果我们需要加上头布局的判断进行头布局的单独实例化创建属于他的ViewHolder
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);
}
我们的BaseViewHolder代码:
/**
* 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;
}
}
现在我们就只需要给每个itemView加上点击事件,这个封装基本就做好了,首先我们新建点击、长按事件的接口
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);
}
然后我们在onBindViewHolder方法中去使用它
@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;
}
好了,到这里封装已经基本可以使用了,下面上完整代码
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;
}
}