Android RecyclerView 使用ItemDecoration实现吸附效果,和业务代码彻底解耦,即插即用

前言

最近在项目开发当中遇到一个记录列表的需求,UED设计稿要求有吸附效果,原本想偷懒在网上找个抄一下,可是简单的看了一下网上的方案都跟业务耦合比较大,不是很想用,就本身写了一个和业务解耦,即插即用的。git

效果图

废话不说,先看东西github

实现的效果仍是不错的。缓存

使用

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

//StickyItemDecoration 实现吸附效果
recyclerView.addItemDecoration(new StickyItemDecoration());
复制代码

实现思路

构造方法

咱们就从这行代码开始分析bash

recyclerView.addItemDecoration(new StickyItemDecoration());
复制代码

首先进入StickyItemDecoration的构造方法ide

public StickyItemDecoration() {
      mStickyView = new ExampleStickyView();
      initPaint();
 }
复制代码

咦,第一行代码是什么意思? 让咱们点击进去看看布局

public class ExampleStickyView implements StickyView {

  @Override
  public boolean isStickyView(View view) {
      return (Boolean) view.getTag();
  }

  @Override
  public int getStickViewType() {
      return 11;
  }
}
复制代码

ExampleStickyView 实现了一个叫作StickyView 的接口,而且须要去实现它的两个方法,那这两个方法是作什么的呢?ui

isStickyView方法 是用来判断传递进来的View是不是须要吸附的View,由于我在适配器当中给须要吸附的View设置了一个tag是true,因此这边代码判断若是tag是true就是须要吸附的View。this

getStickViewType方法,由于须要吸附效果的列表通常都会有2个item type,getStickViewType方法就是返回须要吸附View的type是多少。spa

这两个方法会在ItemDecoration的绘制方onDrawOver法当中用到。设计

接下来就是初始化绘制参数 initPaint();

绘制方法

构造方法结束之后,就要进入到重要的绘制onDrawOver方法

@Override
   public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
       super.onDrawOver(c, parent, state);
       
       //获得当前RecyclerView的布局管理器
       mLayoutManager = (LinearLayoutManager) parent.getLayoutManager();
       mCurrentUIFindStickView = false;

       for (int m = 0, size = parent.getChildCount(); m < size; m++) {
           View view = parent.getChildAt(m);

           /**
            * 这里就用到了ExampleStickyView的isStickyView方法
              用来判断是不是须要吸附效果的View
              是的话才会进入到if逻辑当中
            */
           if (mStickyView.isStickyView(view)) {
               //当前UI当中是否找到了须要吸附的View,此时设置为true
               mCurrentUIFindStickView = true;
               
               //这个方法是获得吸附View的viewHolder
               getStickyViewHolder(parent);
               
               //缓存须要吸附的View在列表当中的下标position 
               cacheStickyViewPosition(m);
               
               //若是当前吸附的view距离 顶部小于等于0,而后给吸附的View绑定数据,计算View的宽高
               if (view.getTop() <= 0) {
                   bindDataForStickyView(mLayoutManager.findFirstVisibleItemPosition(), parent.getMeasuredWidth());
               } else {
               
               //若是大于0,从position缓存中取得当前的position,而后绑定数据,计算View的宽高
                   if (mStickyPositionList.size() > 0) {
                       if (mStickyPositionList.size() == 1) {
                           bindDataForStickyView(mStickyPositionList.get(0), parent.getMeasuredWidth());
                       } else {
                           int currentPosition = getStickyViewPositionOfRecyclerView(m);
                           int indexOfCurrentPosition = mStickyPositionList.lastIndexOf(currentPosition);
                           bindDataForStickyView(mStickyPositionList.get(indexOfCurrentPosition - 1), parent.getMeasuredWidth());
                       }
                   }
               }
               
               //计算吸附的View距离顶部的高度
               if (view.getTop() > 0 && view.getTop() <= mStickyItemViewHeight) {
                   mStickyItemViewMarginTop = mStickyItemViewHeight - view.getTop();
               } else {
                   mStickyItemViewMarginTop = 0;
               }
               
               //绘制吸附的View
               drawStickyItemView(c);
               break;
           }
       }
       
       //若是在当前的列表视图中没有找到须要吸附的View
       if (!mCurrentUIFindStickView) {
           mStickyItemViewMarginTop = 0;
           
           //若是已经滑动到底部了,就绑定最后一个缓存的position的View,这种状况通常出如今快速滑动列表的时候吸附View出现错乱,因此须要绑定一下
           if (mLayoutManager.findFirstVisibleItemPosition() + parent.getChildCount() == parent.getAdapter().getItemCount()) {
               bindDataForStickyView(mStickyPositionList.get(mStickyPositionList.size() - 1), parent.getMeasuredWidth());
           }
           
           //绘制View
           drawStickyItemView(c);
       }
   }
复制代码

上面代码每一行都有注释具体是什么意思,下面我来简单的阐述一下代码的思路和具体逻辑。

大体思路就是:

在列表滚动的时候会进入onDrawOver方法,而后循环当前列表的ItemView,若是遇到是吸附的Item View, 经过适配器再根据itemType来建立一个ViewHolder,而且获得这个ViewHolder的itemView;

循环的时候须要不断去缓存吸附View所在RecyclerView中的下标位置position,根据View距离顶部的高度来获得当前吸附View的position;

接下来经过adapter的onBindViewHolder来给ViewHolder的itemView绑定数据,而后计算itemView的宽高,z这样吸附的View拿到了,数据也绑定好了;

而后再计算距离顶部的高度,把itemView绘制到屏幕上便可。

若是由于在当前列表中没有找到吸附的itemView(mCurrentUIFindStickView=false),就直接绘制上一个便可。

介绍到这里,整理流程就通了,上面贴的并不是所有代码,下面附上源码的连接

https://github.com/chenpengfei88/StickyItemDecoration

有兴趣的朋友能够看看,也欢迎你们star。

相关文章
相关标签/搜索