RecyclerView 上拉加载更多及滚动到底部的判断-第一部分

1. 根据 item 判断是否到达底部 
这种方法最常见,一般都是像下面这样实现:

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;  
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

用这种方式判断是否滚动到底部时,只要最后一个 item 显示出一点,就会触发加载更多,用户此时看不到在 FooterView 处的 加载更多 字样(与拖拽展示出加载更多的需求不符);另外,当 RecyclerView 的 item 过少不足填满整个 RecyclerView 时,也会触发 加载更多 ;因此,这种方式不符合我们的要求。

2. 使用 canScrollVertically(int direction) 判断是否到达底部

RecyclerView.canScrollVertically(1)的值表示是否能向上滚动,false表示已经滚动到底部
RecyclerView.canScrollVertically(-1)的值表示是否能向下滚动,false表示已经滚动到顶部
  • 1
  • 2
  • 3

这种方法看似简单,其实同样存在一些陷阱。当 RecyclerView 的 item 过少不足填满整个 RecyclerView 时,无论上拉还是下拉都会触发加载更多;另外,direction 不只可取1和-1,只需保证正负就能达到一样的效果。

// View#canScrollVertically(int direction) 源码
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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3. 通过 LinearLayoutManager 进行一系列的计算 
这种方法极不推荐使用,过程很复杂,不过对于理解 View 的布局有很大的帮助。这种方法共分为四步,下面将网上的方法抄录如下(请读者自行验证是否也存在方法1,2同样的问题):

  • 算出一个子项的高度

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

判断 RecyclerView 拖拽

这一步比较简单,直接监听滚动即可。

RecyclerView.addOnScrollListener(new OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (newState == SCROLL_STATE_DRAGGING) {
                // 拖拽状态,实际使用中还需要判断 加载更多 是否已显示
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
        }
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

推荐方法

该方法在 View#canScrollVertically(int direction) 的基础上,针对上拉拖拽且有可能 items 没有填充满整个 RecyclerView 这个场景做了优化,代码如下:

RecyclerView.addOnScrollListener(new OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (newState == SCROLL_STATE_DRAGGING && 没有触发加载更多) {
                if (RecyclerView.computeVerticalScrollOffset() > 0) {// 有滚动距离,说明可以加载更多,解决了 items 不能充满 RecyclerView 
                的问题及滑动方向问题
                    boolean isBottom = false ;
                    isBottom = RecyclerView.computeVerticalScrollExtent()
                        + RecyclerView.computeVerticalScrollOffset()
                        == RecyclerView.computeVerticalScrollRange() ;
                    // 也可以使用 方法2
                    // isBottom = !RecyclerView.canScrollVertically(1) ;
                    if (isBottom) {
                        // 说明滚动到底部,触发加载更多
                        ...
                    }
                }
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
        }
    });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值