1. 程式人生 > >總結幾種判斷RecyclerView到達底部的方法

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

640?wx_fmt=png


今日科技快訊


近日,工信部旗下的寬頻發展聯盟給出了《中國寬頻速率狀況》2018年第三季度的報告:第三季度我國固定寬頻平均下載速度是3.12MB/s,上海、北京、江蘇名列前三;全國網速最快的城市,上海市依然位列第一,而北京市、南京市緊隨其後;三大運營商平均固網寬頻速度,其中中國電信速度最快,達到了3.185MB/s,而中國移動位列第二,速度是3.123MB/s,中國聯通最後。


作者簡介


明天就是週六啦,提前祝大家週末愉快!

本篇轉自 19snow93 的部落格,主要研究瞭如何判斷RecyclerView到達底部,對比了幾種方案,希望對大家有所幫助。


本篇文章的原文地址:

https://www.jianshu.com/p/c138055af5d2


正文


之前文章

用事件分發的原理結合SwipeRefreshLayout寫一個RecyclerView的上下拉

https://www.jianshu.com/p/4ef91430009c

裡面有一個判斷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,而這個子項的高度比螢幕還要高。

640?wx_fmt=png

看看效果圖:

640?wx_fmt=png

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

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

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

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

640?wx_fmt=png

這樣就很清晰明瞭,computeVerticalScrollExtent()是當前螢幕顯示的區域高度,computeVerticalScrollOffset() 是當前螢幕之前滑過的距離,而computeVerticalScrollRange()是整個View控制元件的高度。
這種方法經過測試,暫時還沒發現有bug,而且它用的是View自帶的方法,所以個人覺得比較靠譜。

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

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

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

640?wx_fmt=png

是不是一目瞭然了,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有深一步的理解,這也是我的師兄給我提供的一個借鑑的類,我非常感謝他!有興趣的同學可以去下載原始碼的做進一步的研究,發現有更好玩的方法可以一起研究!原始碼地址:

https://github.com/19snow93/SwipeRecyclerView


歡迎長按下圖 -> 識別圖中二維碼

或者 掃一掃 關注我的公眾號

640.png?

640?wx_fmt=jpeg