上一篇文章我講到用事件分發(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)的高度比屏幕還要高。
看看效果圖:
我們可以發(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自帶的方法。
這樣就很清晰明了漠秋,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的源碼:
是不是一目鳥(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)有更好玩的方法可以一起研究!
源碼下載鏈接