總結和分析幾種判斷RecyclerView到達底部的方法

lfs0314 8年前發布 | 114K 次閱讀 Android開發 移動開發 RecyclerView

上一篇文章我講到 用事件分發的原理結合SwipeRefreshLayout寫一個RecyclerView的上下拉 ,里面有一個判斷RecyclerView是否到達底部的方法isBottom。我的同事用了這個上下拉之后發現有些小bug,沒考慮周全,譬如各個子項高度不統一的時候,然后我找到原因是因為這個判斷上下拉的問題。所以,我就去網上查到幾種判斷RecyclerView到達底部的方法,發現各有千秋。以下的分析都以上一篇文章的SwipeRecyclerView為例。

1.lastVisibleItemPosition == totalItemCount - 1判斷;
2.computeVerticalScrollRange()等三個方法判斷;
3.canScrollVertically(1)判斷;
4.利用RecyclerView的LinearLayoutManager幾個方法判斷。

其實,第2和第3種是屬于同一種方法,在下面的分析會講到。

一、首先,我們來介紹和分析一下第一種方法,也是網上最多人用的方法:

public static boolean isVisBottom(RecyclerView recyclerView){  
  LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();  
  //屏幕中最后一個可見子項的position
  int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();  
  //當前屏幕所看到的子項個數
  int visibleItemCount = layoutManager.getChildCount();  
  //當前RecyclerView的所有子項個數
  int totalItemCount = layoutManager.getItemCount();  
  //RecyclerView的滑動狀態
  int state = recyclerView.getScrollState();  
  if(visibleItemCount > 0 && lastVisibleItemPosition == totalItemCount - 1 && state == recyclerView.SCROLL_STATE_IDLE){   
     return true; 
  }else {   
     return false;  
  }
}

很明顯,當屏幕中最后一個子項lastVisibleItemPosition等于所有子項個數totalItemCount - 1,那么RecyclerView就到達了底部。但是,我在這種方法中發現了極為極端的情況,就是當totalItemCount等于1,而這個子項的高度比屏幕還要高。

item_recycleview.png

看看效果圖:

效果圖.png

我們可以發現這個子項沒完全顯示出來就已經被判斷為拉到底部。當然,這種方法一般情況下都能滿足開發者的需求,只是遇到了強迫癥的我~

二、下面我們介紹第二種方法:

public static boolean isSlideToBottom(RecyclerView recyclerView) {    
   if (recyclerView == null) return false; 
   if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset() 
        >= recyclerView.computeVerticalScrollRange())   
     return true;  
   return false;
}

這種方法原理其實很簡單,而且也是View自帶的方法。

原理圖.png

這樣就很清晰明了,computeVerticalScrollExtent()是當前屏幕顯示的區域高度,computeVerticalScrollOffset() 是當前屏幕之前滑過的距離,而computeVerticalScrollRange()是整個View控件的高度。

這種方法經過測試,暫時還沒發現有bug,而且它用的是View自帶的方法,所以個人覺得比較靠譜。

三、下面講講第三種方法:

RecyclerView.canScrollVertically(1)的值表示是否能向上滾動,false表示已經滾動到底部
RecyclerView.canScrollVertically(-1)的值表示是否能向下滾動,false表示已經滾動到頂部

這種方法更簡單,就通過簡單的調用方法,就可以得到你想要的結果。我一講過這種方法與第二種方法其實是同一種方法,那下面來分析一下,看看canScrollVertically的源碼:

canScrollVertically的源碼

是不是一目鳥然了,canScrollVertically方法的實現實際上運用到的是方法二的三個函數,只是這個方法Android已經幫我們封裝好了,原理一模一樣的。

本人現在也是運用了這種方法做判斷的~懶人~工具類都省了~

四、最后一種方法其實是比較呆板的,就是利用LinearLayoutManager的幾個方法,1.算出已經滑過的子項的距離,2.算出屏幕的高度,3.算出RecyclerView的總高度。然后用他們做比較,原理類似于方法二。

public static int getItemHeight(RecyclerView recyclerView) {  
  int itemHeight = 0;  
  View child = null;  
  LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();  
  int firstPos = layoutManager.findFirstCompletelyVisibleItemPosition(); 
  int lastPos = layoutManager.findLastCompletelyVisibleItemPosition();  
  child = layoutManager.findViewByPosition(lastPos);  
  if (child != null) {   
     RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();   
     itemHeight = child.getHeight() + params.topMargin + params.bottomMargin;  
  }   
 return itemHeight;}

算出一個子項的高度

public static int getLinearScrollY(RecyclerView recyclerView) {  
  int scrollY = 0;  
  LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();  
  int headerCildHeight = getHeaderHeight(recyclerView);  
  int firstPos = layoutManager.findFirstVisibleItemPosition();  
  View child = layoutManager.findViewByPosition(firstPos);  
  int itemHeight = getItemHeight(recyclerView);  
  if (child != null) {   
     int firstItemBottom = layoutManager.getDecoratedBottom(child);   
     scrollY = headerCildHeight + itemHeight * firstPos - firstItemBottom;    
     if(scrollY < 0){    
         scrollY = 0;    
     }  
  }  
  return scrollY;
}

算出滑過的子項的總距離

public static int getLinearTotalHeight(RecyclerView recyclerView) {    int totalHeight = 0;  
  LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();  
  View child = layoutManager.findViewByPosition(layoutManager.findFirstVisibleItemPosition());  
  int headerCildHeight = getHeaderHeight(recyclerView);  
  if (child != null) {   
     int itemHeight = getItemHeight(recyclerView);    
     int childCount = layoutManager.getItemCount();    
     totalHeight = headerCildHeight + (childCount - 1) * itemHeight;  
  }  
  return totalHeight;
}

算出所有子項的總高度

public static boolean isLinearBottom(RecyclerView recyclerView) {    
boolean isBottom = true;  
  int scrollY = getLinearScrollY(recyclerView);  
  int totalHeight = getLinearTotalHeight(recyclerView); 
  int height = recyclerView.getHeight();
 //    Log.e("height","scrollY  " + scrollY + "  totalHeight  " +  totalHeight + "  recyclerHeight  " + height);  
  if (scrollY + height < totalHeight) {    
    isBottom = false;  
  }  
  return isBottom;
}

高度作比較

雖然這種方法看上去比較呆板的同時考慮不很周全,但這種方法可以對RecylerView的LinearLayoutManager有深一步的理解,這也是我的師兄給我提供的一個借鑒的類,我非常感謝他!有興趣的同學可以去下載源碼的做進一步的研究,發現有更好玩的方法可以一起研究!

 

 

來自:http://www.jianshu.com/p/c138055af5d2

 

 本文由用戶 lfs0314 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!