上次讲到实现RecyclerView的功能扩展 ,本次就接着介绍针对RecyclerView.Adapter的封装的一些想法。
功能介绍 随着移动端的蓬勃发展,复杂的产品布局也变成了家常便饭,复杂的布局排列组合也成了常态。
比如在拿到这个需求之后,如果放在之前我大多是用ScrollView或者ListView的多Type类型来实现。
如今,采用RecyclerView可以实现一个性能更优的方案,RecyclerView提供了一种插拔式的架构设计,高度的解耦,异常的灵活,也因为这些特性需要调用者自己来实现的代码就相对多一些,所以这也激发了自己想进一步封装一个更好用并且不失灵活性的Adapter。
当然,对于实现以上需求还配合了ItemDecoration来实现间隔,这里就先按下不表。
 
支持多类型布局 
添加/移除头部和尾部 
支持添加点击事件 
配合TurboRecyclerView上拉/左滑加载 
 
接下来,针对以上需求展开介绍。
数据源的添加/删除/重置 大家知道RecyclerView.Adapter是RecyclerView与数据之间的桥梁,所以针对数据源我的想法是在Adapter中维护一个List<T>,这样可以不依赖外部数据源,保证List对Adapter的可见性,在Adapter的封装中仅提供增、删以及重置的方法,所有对数据源操作都通过Adapter中提供的接口来实现增删。 
1 2 3 4 5 6 protected  List<T> mData;... public  BaseTurboAdapter (Context context, List<T> data)  {      this .mData = data == null  ? new  ArrayList <T>() : new  ArrayList <T>(data);       ... } 
下面我们来看看添加数据的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  void  add (T item)  {    boolean  isAdd  =  mData.add(item);     if  (isAdd)         notifyItemInserted(mData.size() + getHeaderViewCount()); } public  void  add (int  position, T item)  {    if  (position < 0  || position > mData.size()) {         Log.e(TAG, "add position = "  + position + ", IndexOutOfBounds, please check your code!" );         return ;     }     mData.add(position, item);     notifyItemInserted(position + getHeaderViewCount()); } public  void  addData (List<T> data)  {    if  (data != null ) {         this .mData.addAll(data);     }     notifyDataSetChanged(); } 
这里需要提一下,针对add(postion,T)中的position是整个Adapter中getItemCount()后需要减去头部的计数,后面会讲到。
删除数据的接口与添加类似,就不在贴代码了,这里我们又提供一个重置数据的方法:
1 2 3 4 public  void  resetData (List<T> data)  {    mData.clear();     addData(data); } 
一般有重新刷新时,请调用这个方法重置数据源。
添加/移除头部和尾部 RecyclerView是一个性能更优的控件,所以对多布局的支持也更好。Header和Footer是配合getItemViewType(position)来实现,我们知道Header、Footer的特点,所以对于头部和尾部的Type类型处理如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public  final  int  getItemViewType (int  position)  {    if  (mHeaderView != null  && position == 0 ) {         return  TYPE_HEADER_VIEW;     } else  if  (position == mData.size() + getHeaderViewCount()) {         if  (mLoading) {             ...         } else  if  (mFooterView != null ) {             return  TYPE_FOOTER_VIEW;         }     }     ... } 
添加/移除头部和尾部会对ItemCount有影响,所以我们需要实现getItemCount,先来看看源码上的注解。
1 2 3 4 5 6  public  abstract  int  getItemCount () ; 
来看看具体的实现:
1 2 3 4 5 6 7 8 9 10 11 12 @Override public  int  getItemCount ()  {    int  count;     if  (mLoading) {          count = mData.size() + 1  + getHeaderViewCount();     } else  {         count = mData.size() + getHeaderViewCount() + getFooterViewCount();     }     ...     return  count; } 
在数据源的基础上计算Header以及Footer的数量并返回。
最后我们来看下针对Header添加、移除(Footer同理):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private  View mHeaderView;... public  void  addHeaderView (View header)  {    if  (header == null ) {         Log.e(TAG, "header is null!!!" );         return ;     }     this .mHeaderView = header;     this .notifyDataSetChanged(); } public  void  removeHeaderView ()  {    if  (mHeaderView != null ) {         this .mHeaderView = null ;         this .notifyDataSetChanged();     } } 
点击事件的处理 在刚开始写这个项目的初期,其实我期望是用一种更简洁的更优雅的方式来处理item点击事件,期间也尝试使用RecyclerView.OnItemTouchListener来实现点击事件,虽然功能上实现了点击事件,但对点击效果的支持没能有效的解决,后来不得不放弃的这一方案。最终,这里还是通过itemView的点击事件来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ... vh = onCreateDefViewHolder(parent, viewType); dispatchItemClickListener(vh); ... private  void  dispatchItemClickListener (final  BaseViewHolder vh)  {    if  (mOnItemClickListeners != null  && mOnItemClickListeners.size() > 0 ) {         if  (!(vh.itemView instanceof  AdapterView)) {             vh.itemView.setOnClickListener(new  View .OnClickListener() {                 @Override                  public  void  onClick (View v)  {                     for  (int  i  =  0 ; i < mOnItemClickListeners.size(); i++) {                         final  OnItemClickListener  listener  =  mOnItemClickListeners.get(i);                         listener.onItemClick(vh, vh.getLayoutPosition() - getHeaderViewCount());                     }                 }             });         }     }     if  (mOnItemLongClickListeners != null  && mOnItemLongClickListeners.size() > 0 ) {         if  (!(vh.itemView instanceof  AdapterView)) {             vh.itemView.setOnLongClickListener(new  View .OnLongClickListener() {                 @Override                  public  boolean  onLongClick (View v)  {                     for  (int  i  =  0 ; i < mOnItemLongClickListeners.size(); i++) {                         final  OnItemLongClickListener  listener  =  mOnItemLongClickListeners.get(i);                         listener.onItemLongClick(vh, vh.getLayoutPosition() - getHeaderViewCount());                     }                     return  true ;                 }             });         }     } } 
对Adapter抽象封装代码上并不复杂,只是基于这些抽象及封装,使用起来更方便快捷。也欢迎支持一下这个项目 ~ 持续维护~😊