RecyclerView 判斷滑到底部 頂部 預加載 更多 分頁 MD


Markdown版本筆記 我的GitHub首頁 我的博客 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

RecyclerView 判斷滑到底部 頂部 預加載 更多 分頁 MD


目錄

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

項目中的案例【預加載】

mRvChat.addOnScrollListener(new RecyclerView.OnScrollListener() {
    private int minLeftItemCount=10;//剩余多少條時開始加載更多
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
            LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
            int itemCount = layoutManager.getItemCount();
            int lastPosition = layoutManager.findLastCompletelyVisibleItemPosition();
            XLog.tag("bqt3").i("【總數】" + itemCount + "【位置】" + lastPosition);
            if (lastPosition == layoutManager.getItemCount() - 1) {//容錯處理,保證滑到最后一條時一定可以加載更多
                this.onLoadMore();
            } else {
                if (itemCount > minLeftItemCount) {
                    if (lastPosition == itemCount - minLeftItemCount) {
                        //一定要意識到,onScrolled方法並不是一直被回調的,估計最多一秒鍾幾十次
                        //所以當此條件滿足時,可能並沒有回調onScrolled方法,也就不會調用onLoadMore方法
                        //所以一定要想辦法彌補這隱藏的bug,最簡單的方式就是當滑到最后一條時一定可以加載更多
                        this.onLoadMore();
                    }
                } else {//(第一次進入時)如果總數特別少,直接加載更多
                    this.onLoadMore();
                }
            }
        }
    }
    private void onLoadMore() {
        if (canLoadMore) {
            canLoadMore = false;
            ChatReqHelper.requestGroupHis(groupId, ((ChatModel) mGroupChats.get(mGroupChats.size() - 1)).getMsgId());
            XLog.tag("bqt3").i("【加載更多數據】");
        }
    }
});

項目中的另一個案例

rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) rv.getLayoutManager();
        int[] positions = layoutManager.findLastVisibleItemPositions(null);
        if (positions != null && positions.length > 0) {
            for (int position : positions) {
                if (position >= mDatas.size() - layoutManager.getSpanCount() || !rv.canScrollVertically(1)) {//滑到底部了
                    if (canLoadMore) {
                        XLog.tag("bqt_red").i("加載更多 "+position+" "+rv.canScrollVertically(1));
                        canLoadMore = false;
                        doRequest();
                    }
                    break;
                }
            }
        }
    }
});

利用 lastVisibleItemPosition 判斷【不靠譜】

簡單判斷

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

當屏幕中最后一個子項 lastVisibleItemPosition 等於所有子項個數 totalItemCount - 1,那么RecyclerView就到達了底部。
但是,如果 totalItemCount 等於1,並且這個item的高度比屏幕還要高時,我們可以發現這個item沒完全顯示出來就已經被判斷為拉到底部。

利用 computeVerticalScrollRange 等判斷【不靠譜】

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

public static boolean isSlideToBottom(RecyclerView recyclerView) {
    return recyclerView != null
            && recyclerView.computeVerticalScrollExtent()//當前屏幕顯示區域的高度
            + recyclerView.computeVerticalScrollOffset()//當前屏幕之前滑過的距離
            >= recyclerView.computeVerticalScrollRange();//整個View控件的高度
}

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

利用 canScrollVertically(direction) 判斷【比較靠譜】

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

/**
 * Check if this view can be scrolled vertically in a certain direction.
 *
 * @param direction Negative to check scrolling up, positive to check scrolling down.
 * @return true if this view can be scrolled in the specified direction, false otherwise.
 */
public boolean canScrollVertically(int direction) {
    final int offset = computeVerticalScrollOffset();
    final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
    if (range == 0) return false;
    if (direction < 0) {
        return offset > 0;
    } else {
        return offset < range - 1;
    }
}

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

利用 LinearLayoutManager 來判斷【垃圾】

這種方法其實是比較呆板的,就是利用LinearLayoutManager的幾個方法算出已經滑過的子項的距離、屏幕的高度、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 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;
}
  • 其他邏輯
public static int getHeaderHeight(RecyclerView recyclerView) {
    int headerCildHeight = 0;

    LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    int firstHeaderPos = layoutManager.findFirstCompletelyVisibleItemPosition();
    View headerCild = layoutManager.findViewByPosition(firstHeaderPos);

    if (headerCild != null) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) headerCild.getLayoutParams();
        headerCildHeight = headerCild.getHeight() + params.topMargin + params.bottomMargin;
    }
    return headerCildHeight;
}
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;
}

雖然這種方法看上去比較呆板的同時考慮不很周全,但這種方法可以對RecylerView的LinearLayoutManager有深一步的理解!

2017.06.23


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM