如何知道RecyclerView / LinearLayoutManager是滚动到顶部还是底部?


79

目前,我正在使用以下代码来检查是否应启用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()
humblerookie

@Saren Arterius,您可能想看看这个
Sheraz Ahmad Khilji,2015年

Answers:


108

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

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 2015年

1
@eluleci:检查列表是否在顶部RecyclerView rc =(RecyclerView)view; LinearLayoutManager lm =(LinearLayoutManager)rc.getLayoutManager(); if(lm.findViewByPosition(lm.findFirstVisibleItemPosition())。getTop()== 0 && lm.findFirstVisibleItemPosition()== 0)返回true; //如果该商品的尺寸大于屏幕尺寸,这将有效
humblerookie 2015年

2
如果项目(第一个或最后一个)大于整个屏幕,则将失败。因为first/lastCompletelyVisibleItemPosition()将返回-1,意味着根本没有完全可见的项。
Subin Sebastian

4
双方lastVisibleItemPositionlastCompletelyVisibleItemPosition没有工作。(无法解析方法)。救命?
星尘

4
findLastCompletelyVisibleItemPosition如果最后一项的高度大于屏幕高度,将失败。这意味着最后一项是可见的,但不是完全可见的layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION)。使用lastVisibleItemPosition并检查布局中最后一个视图的底部会更安全。
消失

78

recyclerView.canScrollVertically(int direction)如果只需要知道是否可以滚动,则可以尝试。

方向整数:

  • -1起
  • 1下
  • 0将始终返回false。

但是,必须间隔一定时间重复一次,对吧?
星尘

1
您可以在recyclerView.addOnScrollListener
..中做到

2
大!当我们到达滚动结束时,这将返回false,我需要此事件。
盒子

1
优秀。在使用这个onScrollStateChanged对我的作品。
Lym Zoy

5
方向整数:-1表示向上,1表示向下,0始终返回false。
Grux

33

为了检查是否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;
    }

一些附加信息

如果你想实现ScrollToBottomRecyclerView时候Edittexttapped那么我建议你加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()虽然
aphoe

1
这对于在recyclerview中只有一项且完全可见的琐碎情况不起作用。
techtinkerer

您保存了一天
Reena

22

@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

只需保留对您的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;
            }
        }
    }
});

1
setOnScrollListner现在已弃用。
Shajeel Afzal

2
您应该使用addOnScrollListener因为@shajeelAfzal所说的setOnScrollListener现在已不赞成使用,您应该使用addOnScrollListener因为@shajeelAfzal所说setOnScrollListener的现在已经不赞成使用,请查看此处
Vasilisfoo 2015年

什么previousTotal
CoolMind

3

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

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

这将检测顶部当您松开手指一度达到最高,如果要检测,尽快的reacyclerview到达顶部检查if(IsRecyclerViewAtTop())里面onScrolled 的方法


3
这似乎是错误的。getChildAt()返回的第一个子ViewGroup元素,该子元素可能(并将)与列表的第一个元素不同。如果该子项恰好与recyclerview的顶部对齐,则您的IsRecyclerViewAtTop()方法将返回true,即使它不在顶部。
Aspyct

2

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

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

}


2

你可以做到这一点对我有用

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

2

要了解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 >= margin }
Renso Lohuis

我的意思是,等于或小于保证金。这样的:fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight <= margin }
Renso Lohuis

1

您可以尝试使用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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.