思路
- ListView中已经自带了添加头布局和添加底部布局的方法,但是在RecyclerView中,却没有默认实现,这导致在实现一些特殊布局中不是那么的方便.
- 在实现RecyclerView.Adapter的时候,有很多相同重复的代码,比如新增或者修改数据源、加载ViewHolder的布局文件
- 本次就是通过封装RecyclerView.Adapter,实现头部(header)、底部(footer)、空页面、ViewHolder.
- 本次封装用到了反射获取布局文件和ViewHolder,泛型指定数据源,使用dataBinding加载数据,使代码更加简洁
- 提供整个cell对外的点击事件
dataBinding的使用
dataBinding {
enabled = true
}
- 不了解dataBinding的使用的,请百度搜索一下
BaseViewHolder的基本封装
- 使用泛型+dataBinding的形式,实现数据的绑定和更新
public class BaseViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
public T t;
/**
* 是否用dataBanding
* @param v
* @param isBinding
*/
public BaseViewHolder(View v,boolean isBinding){
super(v);
if(isBinding){
t = DataBindingUtil.bind(v);
}
}
public BaseViewHolder(View v) {
this(v,true);
}
}
布局文件和ViewHolder的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Adapter {
/**
* 布局文件
* @return
*/
@LayoutRes int layout();
/**
* ViewHolder的class
* @return
*/
Class<? extends BaseViewHolder> holder();
}
定义头部和底部的ViewHolder
/**
* 头部和底部的ViewHolder
*/
static class HeaderAndFooterViewHolder extends BaseViewHolder {
public HeaderAndFooterViewHolder(View itemView) {
super(itemView, false);
}
}
RecyclerAdapter的封装
- 通过反射+注解的方式获取cell的布局文件和ViewHolder的实体
- 定义头部、底部、空页面的不同类型,重写getItemViewType方法,更具下标,是否有头部和底部视图,是否需要加载空页面,来返回不同的类型.
- 重写onCreateViewHolder方法,根据不同的类型创建不同的ViewHolder.
- 重写onBindViewHolder方法,如果当前下标的类型是头部、底部、空页面,就不加载数据源,对外提供cell的点击事件
- GridLayoutManger和StaggeredGridLayoutManager跨列问题
- 代码如下
public abstract class RecyclerAdapter<M, VH extends BaseViewHolder>
extends RecyclerView.Adapter<BaseViewHolder>{
private List<M> mData;
/**
* 头部类型
*/
public static final int TYPE_HEADER = -1;
/**
* 底部类型
*/
public static final int TYPE_FOOTER = -2;
/**
* 无数据类型
*/
public static final int TYPE_EMPTY = -3;
/**
* 正常的item
*/
public static final int TYPE_NORMAL = 0;
/**
* 头部
*/
private View mHeaderView;
/**
* 底部
*/
private View mFooterView;
/**
* 是否展示空页面
*/
private boolean showEmpty;
/**
* 空页面layout
*/
private int emptyLayout = R.layout.view_load_empty;
/**
* 是否是第一次加载数据
*/
private boolean firstLoad = true;
private RecyclerAdapter.OnItemClickListener mListener;
/**
* 分页处理器
*/
private IPageControl mPageControl;
public RecyclerAdapter() {
this(null);
}
public RecyclerAdapter(IPageControl pageControl) {
mData = new ArrayList<>();
mPageControl = pageControl;
annotationAdapter();
}
private Adapter mAdapterAnnotation;
/**
* 获取@Adapter注解
*/
private void annotationAdapter() {
Class cls = this.getClass();
if (cls.isAnnotationPresent(Adapter.class)) {
mAdapterAnnotation = this.getClass().getAnnotation(Adapter.class);
}
}
/**
* 设置空页面
*/
public void setEmptyLayout(@LayoutRes int emptyLayout) {
this.emptyLayout = emptyLayout;
}
/**
* 添加头部
*/
public void setHeaderView(View headerView) {
mHeaderView = headerView;
}
/**
* 获取头部
*/
public View getHeaderView() {
return mHeaderView;
}
/**
* 添加底部
*/
public void setFooterView(View footerView) {
mFooterView = footerView;
}
/**
* 获取底部
*/
public View getFooterView() {
return mFooterView;
}
/**
* 设置每行点击事件的监听
*/
public void setOnItemClickListener(RecyclerAdapter.OnItemClickListener listener) {
mListener = listener;
}
public List<M> getData() {
if (ListUtils.isEmpty(mData)) {
throw new IllegalStateException("mData is empty(数据为空)");
}
return mData;
}
public M get(int position) {
if (position < 0 || position > (mData.size() - 1)) {
throw new IllegalStateException("position必须大于0,且不能大于mData的个数");
}
if (ListUtils.isEmpty(mData)) {
return null;
}
return mData.get(position);
}
/**
* 设置list为这个list
*/
public void set(List<M> data) {
firstLoad = false;
if (data != null) {
mData = data;
}
notifyDataSetChanged();
}
/**
* 清空数据
*/
public void clear() {
mData.clear();
notifyDataSetChanged();
}
/**
* list中添加更多的数据
*/
public void add(List<M> data) {
if (mData == null) {
return;
}
mData.addAll(data);
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (position == 0 && showEmpty) {
//当前数据空位,展示空页面
return TYPE_EMPTY;
}
if (position == 0 && mHeaderView != null) {
//当前view是头部信息
return TYPE_HEADER;
}
if (position == getItemCount() - 1 && mFooterView != null) {
//当前view是底部信息
return TYPE_FOOTER;
}
return getCenterViewType(position);
}
/**
* 标准的item的类型
*
* @return 返回参数不能小于0
*/
@IntRange(from = 0)
public int getCenterViewType(int position) {
return TYPE_NORMAL;
}
@Override
public int getItemCount() {
int size = mData == null ? 0 : mData.size();
if (mHeaderView != null) {
//有头部,item的个数+1
size++;
}
if (mFooterView != null) {
//有底部,item的个数+1
size++;
}
if (size == 0) {
showEmpty = true;
size = 1;
} else {
showEmpty = false;
}
return size;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//加载头部信息
if (TYPE_HEADER == viewType) {
return new HeaderAndFooterViewHolder(mHeaderView);
}
//加载底部信息
if (TYPE_FOOTER == viewType) {
return new HeaderAndFooterViewHolder(mFooterView);
}
//加载空页面
if (TYPE_EMPTY == viewType) {
View v = inflate(emptyLayout, parent);
return new HeaderAndFooterViewHolder(v);
}
//反射获取ViewHolder
if (mAdapterAnnotation != null) {
return reflectViewHolder(parent);
}
return createHolder(parent, viewType);
}
/**
* 反射获得ViewHolder
*/
@SuppressWarnings("unchecked")
private VH reflectViewHolder(ViewGroup parent) {
View v = inflate(mAdapterAnnotation.layout(), parent);
Class<VH> c = (Class<VH>) mAdapterAnnotation.holder();
VH holder = null;
try {
Constructor<VH> con = c.getConstructor(View.class);
holder = con.newInstance(v);
} catch (NoSuchMethodException e) {
System.err.println("检查ViewHolder类及构造函数是否是public");
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return holder;
}
/**
* 除头部和底部的ViewHolder的获取
*
* @param viewType holder的类型
*/
protected VH createHolder(ViewGroup parent, int viewType) {
return null;
}
/**
* 获取需要viewHolder的view
*
* @param layoutId 布局文件
*/
protected View inflate(int layoutId, ViewGroup group) {
LayoutInflater inflater = LayoutInflater.from(group.getContext());
return inflater.inflate(layoutId, group, false);
}
@SuppressWarnings("unchecked")
@Override
public void onBindViewHolder(BaseViewHolder holder, final int position) {
int index = position;
if (mHeaderView != null) {
//当前holder是头部就直接返回,不需要去设置viewholder的内容
if (getItemViewType(position) == TYPE_HEADER) {
return;
} else {
/*
* 有头部的情况,需要要减1,否则取item的数据会取到当前数据的下一条,
* 取出最后一条数据的时候,会报下标溢出
*/
index--;
}
}
if (mFooterView != null) {
//当前holder是底部就直接返回,不需要去设置viewholder的内容
if (getItemViewType(position) == TYPE_FOOTER) {
return;
}
}
//空页面状态,不需要设置holder的内容
if (getItemViewType(position) == TYPE_EMPTY) {
//第一次加载数据,不展示空页面
if (firstLoad) {
holder.itemView.setVisibility(View.INVISIBLE);
} else {
holder.itemView.setVisibility(View.VISIBLE);
}
return;
}
final int finalIndex = index;
if (mData == null || mData.isEmpty() || index < 0 || index > mData.size() - 1) {
return;
}
M m = mData.get(index);
//设置item的点击回调事件
holder.itemView.setOnClickListener(v -> {
if (mListener != null) {
mListener.itemClick(mRecyclerView.getId(), m, finalIndex);
}
});
bindViewHolder((VH) holder, m, position);
}
/**
* 绑定viewHolder的数据
*/
public abstract void bindViewHolder(VH holder, M m, int position);
private RecyclerView mRecyclerView;
/**
* 处理RecyclerView.LayoutManager是GridLayoutManager类型,
* 对头部和底部实体进行一个处理,使其占满一行
* @param recyclerView
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return (getItemViewType(position) == TYPE_HEADER
|| getItemViewType(position) == TYPE_FOOTER)
? gridManager.getSpanCount() : 1;
}
});
}
}
/**
* 加入针对StaggeredGridLayoutManager跨列处理的代码
* 一个item通过adapter开始显示会被回调
* @param holder
*/
@Override
public void onViewAttachedToWindow(BaseViewHolder holder) {
super.onViewAttachedToWindow(holder);
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
if (lp != null
&& lp instanceof StaggeredGridLayoutManager.LayoutParams
&& holder.getLayoutPosition() == 0) {
StaggeredGridLayoutManager.LayoutParams p =
(StaggeredGridLayoutManager.LayoutParams) lp;
//占满一行
p.setFullSpan(true);
}
}
/**
* 头部和底部的ViewHolder
*/
static class HeaderAndFooterViewHolder extends BaseViewHolder {
public HeaderAndFooterViewHolder(View itemView) {
super(itemView, false);
}
}
/**
* item点击事件
*/
public interface OnItemClickListener<M> {
/**
* @param id RecyclerView.getId()
* @param m item下的实体
* @param position item所在的位置
*/
void itemClick(@IdRes int id, M m, int position);
}
}