使用ItemDecoration實現吸附效果和業務代碼完全解耦即插即用 [復制鏈接]

2018-9-14 10:21
sergiochanTest 閱讀:583 評論:2 贊:0
Tag:  

前言

最近在項目開發當中遇到一個記錄列表的需求,UED設計稿要求有吸附效果,本來想偷懶在網上找個抄一下,但是簡單的看了一下網上的方案都跟業務耦合比較大,不是很想用,就自己寫了一個和業務解耦,即插即用的。

效果圖

廢話不說,先看東西

實現的效果還是不錯的。

使用

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

//StickyItemDecoration 實現吸附效果
recyclerView.addItemDecoration(new StickyItemDecoration());

實現思路

構造方法

我們就從這行代碼開始分析

recyclerView.addItemDecoration(new StickyItemDecoration());

首先進入StickyItemDecoration的構造方法

 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 的接口,并且需要去實現它的兩個方法,那這兩個方法是做什么的呢?

isStickyView方法 是用來判斷傳遞進來的View是否是需要吸附的View,因為我在適配器當中給需要吸附的View設置了一個tag是true,所以這邊代碼判斷如果tag是true就是需要吸附的View。

getStickViewType方法,因為需要吸附效果的列表一般都會有2個item type,getStickViewType方法就是返回需要吸附View的type是多少。

這兩個方法會在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),就直接繪制上一個即可。



我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(2)
sjf_xiaobai 2018-9-14 11:28
大師
回復
成love瑞 2018-9-17 10:44
就不能放個demo出來么?適配器的代碼也沒貼
回復
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

两码中特期期