【问题标题】:How to know whether a RecyclerView / LinearLayoutManager is scrolled to top or bottom?如何知道 RecyclerView / LinearLayoutManager 是滚动到顶部还是底部?
【发布时间】:2015-03-06 15:54:42
【问题描述】:

目前我正在使用以下代码来检查是否应该启用 SwipeRefreshLayout。

private void laySwipeToggle() {
    if (mRecyclerView.getChildCount() == 0 || mRecyclerView.getChildAt(0).getTop() == 0) {
        mLaySwipe.setEnabled(true);
    } else {
        mLaySwipe.setEnabled(false);
    }
}

但这就是问题所在。当它滚动到另一个项目的视图边界时,mRecyclerView.getChildAt(0).getTop() 也返回 0。

RecyclerView.isScrolledToBottom()RecyclerView.isScrolledToTop() 之类的吗?

编辑:(mRecyclerView.getChildAt(0).getTop() == 0 && linearLayoutManager.findFirstVisibleItemPosition() == 0) 有点像 RecyclerView.isScrolledToTop(),但是 RecyclerView.isScrolledToBottom() 呢?

【问题讨论】:

  • 我想后者可以通过针对最后一个孩子验证 recyclerview 的底部来实现,即 mRecyclerView.getBottom()== linearLayoutmanager.findViewbyPosition(adapter.getItemCount() - 1 ).getBottom()
  • @Saren Arterius 你可能想看看this

标签: android android-recyclerview


【解决方案1】:

解决方案在布局管理器中。

LinearLayoutManager layoutManager = new LinearLayoutManager(this);

// Add this to your Recycler view
recyclerView.setLayoutManager(layoutManager);

// To check if at the top of recycler view
if(layoutManager.firstCompletelyVisibleItemPosition()==0){
    // Its at top
}

// To check if at the bottom of recycler view
if(layoutManager.lastCompletelyVisibleItemPosition()==data.size()-1){
    // Its at bottom
}

编辑

如果您的项目大小大于屏幕,请使用以下方法检测顶部事件。

RecyclerView recyclerView = (RecyclerView) view;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

int pos = linearLayoutManager.findFirstVisibleItemPosition();

if(linearLayoutManager.findViewByPosition(pos).getTop()==0 && pos==0){
    return true;
}

PS:实际上,如果您将RecyclerView 直接放在SwipeRefreshview 中,则不需要这样做

【讨论】:

  • 不,当 RecyclerView 是 SwipeRefreshLayout 的直接子级时,仍然会发生这种情况。但是检查 firstCompletelyVisibleItemPosition 可以解决问题。我需要在这里指出一些东西。如果没有一个项目是完全可见的,firstCompletelyVisibleItemPosition() 返回 -1。因此,如果第一个项目在滚动到顶部时不适合 RecyclerView,则此解决方案将不起作用。但这是一种非常罕见的情况。
  • @eluleci : 检查列表是否在顶部 RecyclerView rc=(RecyclerView)view; LinearLayoutManager lm= (LinearLayoutManager) rc.getLayoutManager(); if(lm.findViewByPosition(lm.findFirstVisibleItemPosition()).getTop()==0 && lm.findFirstVisibleItemPosition()==0) 返回真; //如果项目的尺寸大于屏幕尺寸,这将起作用
  • 如果项目(第一个或最后一个)大于整个屏幕,这将失败。因为,first/lastCompletelyVisibleItemPosition() 将返回 -1,这意味着根本没有完全可见的项目。
  • lastVisibleItemPositionlastCompletelyVisibleItemPosition 都不起作用。 (无法解析方法)。帮忙?
  • 如果最后一项的高度大于屏幕高度,findLastCompletelyVisibleItemPosition 将失败。这意味着最后一项是可见的,但不是完全可见的,所以layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION)。使用lastVisibleItemPosition 并检查布局中最后一个视图的底部会更安全。
【解决方案2】:

你可以试试recyclerView.canScrollVertically(int direction),如果你只是想知道是否可以滚动。

方向整数:

  • -1 向上
  • 1 表示向下
  • 0 将始终返回 false。

【讨论】:

  • 这必须在一定间隔内重复,对吧?
  • 你可以在 recyclerView.addOnScrollListener 中解决这个问题。它就像一个魅力......谢谢
  • 太棒了!当我们到达滚动结束时,这会返回一个 false,我需要这个事件。
  • 优秀。在onScrollStateChanged 中使用它对我有用。
  • 方向整数:-1 向上,1 向下,0 将始终返回 false。
【解决方案3】:

为了检查RecyclerView是否滚动到底部。使用以下代码。

/**
     * Check whether the last item in RecyclerView is being displayed or not
     *
     * @param recyclerView which you would like to check
     * @return true if last position was Visible and false Otherwise
     */
    private boolean isLastItemDisplaying(RecyclerView recyclerView) {
        if (recyclerView.getAdapter().getItemCount() != 0) {
            int lastVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
            if (lastVisibleItemPosition != RecyclerView.NO_POSITION && lastVisibleItemPosition == recyclerView.getAdapter().getItemCount() - 1)
                return true;
        }
        return false;
    }

一些附加信息

如果您想在Edittexttapped 时在RecyclerView 中实现ScrollToBottom,那么我建议您像这样添加1 秒延迟

 edittext.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP)
                    if (isLastItemDisplaying(recyclerView)) {
// The scrolling can happen instantly before keyboard even opens up so to handle that we add 1 second delay to scrolling
                        recyclerView.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount() - 1);

                            }
                        }, 1000);
                    }
                return false;
            }
        });

【讨论】:

  • 为我工作。我不得不将它添加到 RecyclerView 的 addOnScrollListener() 虽然
  • 这不适用于在 recyclerview 中有一个项目并且完全可见的琐碎情况。
  • 你节省了一天
【解决方案4】:

@Saren Arterius,使用 RecycleViewaddOnScrollListener,您可以找到 Vertical RecycleView 的滚动顶部或底部,如下所示,

RecyclerView rv = (RecyclerView)findViewById(R.id.rv);

rv.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

                if (dy < 0) {
                    // Recycle view scrolling up...

                } else if (dy > 0) {
                    // Recycle view scrolling down...
                }
            }
        });

【讨论】:

    【解决方案5】:

    使用 recyclerView.canScrollVertically(int direction) 检查是否到达了滚动的顶部或底部。

    direction = 1 向下滚动(底部)

    direction = -1 向上滚动(顶部)

    如果方法返回 false 表示您到达顶部或底部取决于方向。

    【讨论】:

    • 非常感谢它成功了!
    【解决方案6】:

    只需保留对您的 layoutManager 的引用并像这样在您的回收站视图上设置 onScrollListener

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
    
            visibleItemCount = mRecyclerView.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            firstVisibleItemIndex = mLayoutManager.findFirstVisibleItemPosition();
    
            //synchronizew loading state when item count changes
            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false;
                    previousTotal = totalItemCount;
                }
            }
            if (!loading)
                if ((totalItemCount - visibleItemCount) <= firstVisibleItemIndex) {
                    // Loading NOT in progress and end of list has been reached
                    // also triggered if not enough items to fill the screen
                    // if you start loading
                    loading = true;
                } else if (firstVisibleItemIndex == 0){
                    // top of list reached
                    // if you start loading
                    loading = true;
                }
            }
        }
    });
    

    【讨论】:

    • setOnScrollListner 现已弃用。
    • 你应该使用addOnScrollListener,因为@shajeelAfzal 说setOnScrollListener 现在已弃用,你应该使用addOnScrollListener,因为@shajeelAfzal 说setOnScrollListener 现在已弃用,请检查here跨度>
    • 什么是previousTotal
    【解决方案7】:

    你可以做到这对我有用

          mRecycleView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                }
            }
    
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ?
                            0 : recyclerView.getChildAt(0).getTop();
                    LinearLayoutManager linearLayoutManager1 = (LinearLayoutManager) recyclerView.getLayoutManager();
                    int firstVisibleItem = linearLayoutManager1.findFirstVisibleItemPosition();
                    swipeRefreshLayout.setEnabled(firstVisibleItem == 0 && topRowVerticalPosition >= 0);
                }
            });
    

    【讨论】:

    • 这对我有用。谢谢。
    【解决方案8】:

    您可以使用以下方法检测顶部

     recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
              }
    
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if(isRecyclerViewAtTop())
                {
                    //your recycler view reached Top do some thing
                 }
            }
        });
    
      private boolean isRecyclerViewAtTop()   {
            if(recyclerView.getChildCount() == 0)
                return true;
            return recyclerView.getChildAt(0).getTop() == 0;
        }
    

    这将在您松开手指到达顶部时检测到顶部, 如果你想尽快检测到 recyclerview 到达顶部检查 if(isRecyclerViewAtTop())onScrolled 方法

    【讨论】:

    • 这似乎是错误的。 getChildAt() 返回ViewGroup 的第一个子元素,它可能(也将)不同于列表的第一个元素。如果该子对象恰好与 recyclerview 的顶部完全对齐,您的 IsRecyclerViewAtTop() 方法将返回 true,即使它不在顶部。
    【解决方案9】:

    我写了一个 RecyclerViewHelper 来知道 r​​ecyclerview 是在顶部还是在底部。

    public class RecyclerViewHelper {
    public static boolean isAtTop(RecyclerView recyclerView) {
        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            return isAtTopBeforeIceCream(recyclerView);
        } else {
            return !ViewCompat.canScrollVertically(recyclerView, -1);
        }
    }
    
    private static boolean isAtTopBeforeIceCream(RecyclerView recyclerView) {
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof LinearLayoutManager) {
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
            int pos = linearLayoutManager.findFirstVisibleItemPosition();
            if (linearLayoutManager.findViewByPosition(pos).getTop() == recyclerView.getPaddingTop() && pos == 0)
                return true;
        }
        return false;
    }
    
    
    public static boolean isAtBottom(RecyclerView recyclerView) {
        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            return isAtBottomBeforeIceCream(recyclerView);
        } else {
            return !ViewCompat.canScrollVertically(recyclerView, 1);
        }
    }
    
    private static boolean isAtBottomBeforeIceCream(RecyclerView recyclerView) {
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        int count = recyclerView.getAdapter().getItemCount();
        if (layoutManager instanceof LinearLayoutManager) {
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
            int pos = linearLayoutManager.findLastVisibleItemPosition();
            int lastChildBottom = linearLayoutManager.findViewByPosition(pos).getBottom();
            if (lastChildBottom == recyclerView.getHeight() - recyclerView.getPaddingBottom() && pos == count - 1)
                return true;
        }
        return false;
    }
    

    }

    【讨论】:

      【解决方案10】:

      要查看 RecyclerView 是否滚动到底部,您可以重用用于滚动条的方法。

      这是计算,为了方便写成 Kotlin 扩展函数:

      fun RecyclerView.isScrolledToBottom(): Boolean {
          val contentHeight = height - (paddingTop + paddingBottom)
          return computeVerticalScrollRange() == computeVerticalScrollOffset() + contentHeight
      }
      

      【讨论】:

      • 不错的解决方案!我看到了一些滚动大小只有 1 的问题,即使我在底部滚动(可能是一些舍入问题)所以我更改了代码以使用边距:fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight &gt;= margin }
      • 我的意思是,小于或等于边距。像这样:fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight &lt;= margin }
      【解决方案11】:

      您可以尝试使用 OnTouchListener:

      recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
          @Override
          public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
              if (e.getAction() == MotionEvent.ACTION_UP
                  || e.getAction() == MotionEvent.ACTION_MOVE){
              if (mLinearLayoutManager.findFirstCompletelyVisibleItemPosition() > 0)
              {
              // beginning of the recycler 
              }
      
              if (mLinearLayoutManager.findLastCompletelyVisibleItemPosition()+1 < recyclerView.getAdapter().getItemCount())
              {
              // end of the recycler 
              }         
           }             
      return false;
      }
      

      【讨论】:

        【解决方案12】:

        如果你还在寻找 2022 年的答案,那就去吧:

         mRecyclerView.setOnScrollChangeListener((view, i, i1, i2, i3) -> {
                    if(!mRecyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)){
                         // reached the bottom of the list, load more data
                    }
                });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-04
          • 1970-01-01
          • 2014-07-11
          • 1970-01-01
          • 1970-01-01
          • 2017-06-26
          相关资源
          最近更新 更多