總結(jié)和分析幾種判斷RecyclerView到達(dá)底部的方法

上一篇文章我講到用事件分發(fā)的原理結(jié)合SwipeRefreshLayout寫(xiě)一個(gè)RecyclerView的上下拉,里面有一個(gè)判斷RecyclerView是否到達(dá)底部的方法isBottom饵较。我的同事用了這個(gè)上下拉之后發(fā)現(xiàn)有些小bug,沒(méi)考慮周全遭赂,譬如各個(gè)子項(xiàng)高度不統(tǒng)一的時(shí)候循诉,然后我找到原因是因?yàn)檫@個(gè)判斷上下拉的問(wèn)題。所以撇他,我就去網(wǎng)上查到幾種判斷RecyclerView到達(dá)底部的方法茄猫,發(fā)現(xiàn)各有千秋。以下的分析都以上一篇文章的SwipeRecyclerView為例逆粹。

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

其實(shí)他嚷,第2和第3種是屬于同一種方法蹋绽,在下面的分析會(huì)講到芭毙。

一、首先卸耘,我們來(lái)介紹和分析一下第一種方法退敦,也是網(wǎng)上最多人用的方法:

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

很明顯,當(dāng)屏幕中最后一個(gè)子項(xiàng)lastVisibleItemPosition等于所有子項(xiàng)個(gè)數(shù)totalItemCount - 1蚣抗,那么RecyclerView就到達(dá)了底部侈百。但是,我在這種方法中發(fā)現(xiàn)了極為極端的情況翰铡,就是當(dāng)totalItemCount等于1钝域,而這個(gè)子項(xiàng)的高度比屏幕還要高。

item_recycleview.png

看看效果圖:

效果圖.png

我們可以發(fā)現(xiàn)這個(gè)子項(xiàng)沒(méi)完全顯示出來(lái)就已經(jīng)被判斷為拉到底部锭魔。當(dāng)然例证,這種方法一般情況下都能滿足開(kāi)發(fā)者的需求,只是遇到了強(qiáng)迫癥的我~

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

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

這種方法原理其實(shí)很簡(jiǎn)單织咧,而且也是View自帶的方法。

原理圖.png

這樣就很清晰明了漠秋,computeVerticalScrollExtent()是當(dāng)前屏幕顯示的區(qū)域高度笙蒙,computeVerticalScrollOffset() 是當(dāng)前屏幕之前滑過(guò)的距離,而computeVerticalScrollRange()是整個(gè)View控件的高度庆锦。
這種方法經(jīng)過(guò)測(cè)試手趣,暫時(shí)還沒(méi)發(fā)現(xiàn)有bug,而且它用的是View自帶的方法肥荔,所以個(gè)人覺(jué)得比較靠譜绿渣。

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

RecyclerView.canScrollVertically(1)的值表示是否能向上滾動(dòng)燕耿,false表示已經(jīng)滾動(dòng)到底部
RecyclerView.canScrollVertically(-1)的值表示是否能向下滾動(dòng)中符,false表示已經(jīng)滾動(dòng)到頂部

這種方法更簡(jiǎn)單,就通過(guò)簡(jiǎn)單的調(diào)用方法誉帅,就可以得到你想要的結(jié)果淀散。我一講過(guò)這種方法與第二種方法其實(shí)是同一種方法,那下面來(lái)分析一下蚜锨,看看canScrollVertically的源碼:

canScrollVertically的源碼

是不是一目鳥(niǎo)然了档插,canScrollVertically方法的實(shí)現(xiàn)實(shí)際上運(yùn)用到的是方法二的三個(gè)函數(shù),只是這個(gè)方法Android已經(jīng)幫我們封裝好了亚再,原理一模一樣的郭膛。
本人現(xiàn)在也是運(yùn)用了這種方法做判斷的懶人工具類(lèi)都省了~

四、最后一種方法其實(shí)是比較呆板的氛悬,就是利用LinearLayoutManager的幾個(gè)方法则剃,1.算出已經(jīng)滑過(guò)的子項(xiàng)的距離耘柱,2.算出屏幕的高度,3.算出RecyclerView的總高度棍现。然后用他們做比較调煎,原理類(lèi)似于方法二。

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;}

算出一個(gè)子項(xiàng)的高度

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;
}

算出滑過(guò)的子項(xiàng)的總距離

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;
}

算出所有子項(xiàng)的總高度

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;
}

高度作比較
雖然這種方法看上去比較呆板的同時(shí)考慮不很周全己肮,但這種方法可以對(duì)RecylerView的LinearLayoutManager有深一步的理解士袄,這也是我的師兄給我提供的一個(gè)借鑒的類(lèi),我非常感謝他谎僻!有興趣的同學(xué)可以去下載源碼的做進(jìn)一步的研究娄柳,發(fā)現(xiàn)有更好玩的方法可以一起研究!
源碼下載鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戈稿,一起剝皮案震驚了整個(gè)濱河市西土,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鞍盗,老刑警劉巖需了,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異般甲,居然都是意外死亡肋乍,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)敷存,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)墓造,“玉大人,你說(shuō)我怎么就攤上這事锚烦∶倜觯” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵涮俄,是天一觀的道長(zhǎng)蛉拙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)彻亲,這世上最難降的妖魔是什么孕锄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮苞尝,結(jié)果婚禮上畸肆,老公的妹妹穿的比我還像新娘。我一直安慰自己宙址,他們只是感情好轴脐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般豁辉。 火紅的嫁衣襯著肌膚如雪令野。 梳的紋絲不亂的頭發(fā)上舀患,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天徽级,我揣著相機(jī)與錄音,去河邊找鬼聊浅。 笑死餐抢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的低匙。 我是一名探鬼主播旷痕,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顽冶!你這毒婦竟也來(lái)了欺抗?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤强重,失蹤者是張志新(化名)和其女友劉穎绞呈,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體间景,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡佃声,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倘要。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圾亏。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖封拧,靈堂內(nèi)的尸體忽然破棺而出志鹃,到底是詐尸還是另有隱情,我是刑警寧澤泽西,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布曹铃,位于F島的核電站,受9級(jí)特大地震影響尝苇,放射性物質(zhì)發(fā)生泄漏铛只。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一糠溜、第九天 我趴在偏房一處隱蔽的房頂上張望淳玩。 院中可真熱鬧,春花似錦非竿、人聲如沸蜕着。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)承匣。三九已至蓖乘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間韧骗,已是汗流浹背嘉抒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袍暴,地道東北人些侍。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像政模,于是被迫代替她去往敵國(guó)和親岗宣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容