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。

相關文章
相關標籤/搜索