思路

  • ListView中已经自带了添加头布局和添加底部布局的方法,但是在RecyclerView中,却没有默认实现,这导致在实现一些特殊布局中不是那么的方便.
  • 在实现RecyclerView.Adapter的时候,有很多相同重复的代码,比如新增或者修改数据源、加载ViewHolder的布局文件
  • 本次就是通过封装RecyclerView.Adapter,实现头部(header)、底部(footer)、空页面、ViewHolder.
  • 本次封装用到了反射获取布局文件和ViewHolder,泛型指定数据源,使用dataBinding加载数据,使代码更加简洁
  • 提供整个cell对外的点击事件

dataBinding的使用

  • 需要在gradle文件里面添加
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();}@Overridepublic 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;}@Overridepublic int getItemCount() {int size = mData == null ? 0 : mData.size();if (mHeaderView != null) {//有头部,item的个数+1size++;}if (mFooterView != null) {//有底部,item的个数+1size++;}if (size == 0) {showEmpty = true;size = 1;} else {showEmpty = false;}return size;}@Overridepublic 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);}//反射获取ViewHolderif (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")@Overridepublic 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*/@Overridepublic 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() {@Overridepublic int getSpanSize(int position) {return (getItemViewType(position) == TYPE_HEADER|| getItemViewType(position) == TYPE_FOOTER)? gridManager.getSpanCount() : 1;}});}}/*** 加入针对StaggeredGridLayoutManager跨列处理的代码* 一个item通过adapter开始显示会被回调* @param holder*/@Overridepublic 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);}
}
复制代码

RecyclerView.Adapter的封装(RecyclerAdapter)相关推荐

  1. RecyclerView Adapter 优雅封装搞定所有列表

    转载自: 依然范特稀西 RecycleView加载列表,封装Adapter,快速高效的添加一个列表(包括单 Item 列表和多item列表). 理念 1, 构造一个通用的Adapter模版,避免每添加 ...

  2. Android 封装RecyclerView.Adapter,省其ViewHolder

    RecyclerView大家都使用过的话会有些不方便,现在对其封装一下,让其可以: 1.可以像ListView一样方便使用OnItemClickListener: 2.可省去ViewHolder,快速 ...

  3. RecyclerView.Adapter:全能notify解决方案

    原文链接: https://loshine.me/2016/08/25/a-universal-solution-of-recyclerview-adapter-notify/ 在之前我们用 List ...

  4. kotlin中RecyclerView.Adapter通用适配器

    文章目录 结合databinding,livedata BaseViewHolder BaseAdapter recycleView通用adapter 使用 数据bean ViewModel 绑定it ...

  5. RecyclerView.Adapter通用基类

    RecyclerView.Adapter通用基类 一般来说,我们在使用RecyclerView的时候,需要自定义一个Adapter,用来适配RecyclerView和data.这里主要来编写这个Ada ...

  6. android电脑文件列表不刷新,Android 利用RecyclerView.Adapter刷新列表中的单个view问题...

    首先使用RecyclerView的adapter继承:RecyclerView.Adapter public class OrderListAdapter extends RecyclerView.A ...

  7. android activity调用Adapter方法刷新列表UI,RecyclerView.Adapter

    在adapter中创建被调用方法cleckAll public class JYfkleixinAdapter extends RecyclerView.Adapter<JYfkleixinAd ...

  8. RecyclerView.Adapter notifyDataSetChanged 不起作用

    最近项目里要添加个聊天功能,我们使用的是环信SDK. 如果应用启动,不在聊天界面,接收到消息后就弹出通知栏消息通知用户,点击进入聊天界面. 如果用户已经在聊天界面,就要将接收到的数据添加到adapte ...

  9. android 中自定义安装,Android开发中ListView自定义adapter的封装

    [引入] 我们一般编写listView的时候顺序是这样的: •需要展示的数据集List •为这个数据集编写一个ListView •为这个ListView编写一个Adapter,一般继承自BaseAda ...

最新文章

  1. 双活方案_MySQL业务双活的初步设计方案
  2. SVN的标准目录结构:trunk、branches、tags
  3. python画蜡烛致敬烈士_「」matplotlib 股票-用python绘制蜡烛线型k线图是用代码还是绘图工具-TOP金融网...
  4. selenium python下载_使用Selenium、Chrome和Python下载PDF
  5. 剑三千岛湖服务器是不是维护了,《剑网3》4.10维护公告:疆域重新进行地理划归...
  6. 20160319中艺收盘总结
  7. Winform打包-NSIS 检测.NetFramework版本(亲测可用)
  8. 不需编译让aspx页自主筛选数据绑定记录
  9. AX2012/D365 批处理如何创建
  10. HYSPLIT 模型 传输轨迹 使用指南
  11. 什么是tomcat?tomcat是干什么用的?下面带你们认识tomcat!通俗易懂!
  12. 如何使用数据分析客户购买意愿
  13. 【算法导论-34】红黑树、顺序统计树的Java实现
  14. Redis_数据类型(常用)
  15. dismiss和remove_Dialog的dismiss和cancel 区别 (转)
  16. 成功的礼品公司的产品经营模式
  17. 完美解决KMplayer无法播放RMVB、RM电影问题
  18. 一位清华计算机专业的学生怎么看LINUX与WINDOWS
  19. layui.table 头部工具栏与行工具栏的实例
  20. matlab 平均梯度,OpenCV 自学笔记33. 计算图像的均值、标准差和平均梯度

热门文章

  1. 用css去除chrome、safari等webikt内核浏览器对控件默认样式
  2. 前端设计必备-Font awesome 插件使用菜鸟言语
  3. 热门开源CI/CD解决方案 GoCD 中曝极严重漏洞,可被用于接管服务器并执行任意代码...
  4. 从业余挖洞到微软漏洞研究员,我的遗憾、惊喜和建议
  5. 厄瓜多尔最大银行遭攻击,服务被迫中断
  6. “机智号”成功试飞火星,但它使用的开源软件安全吗?
  7. 奇安信代码安全实验室帮助谷歌修复 Chrome 沙箱外高危漏洞,获官方致谢
  8. LG 源代码或被盗,如何才能毫发无损地要回来?
  9. Java 学习笔记之 Synchronized锁重入
  10. HTTP 返回304