Как узнать, прокручивается ли RecyclerView/LinearLayoutManager вверх или вниз?

В настоящее время я использую следующий код, чтобы проверить, следует ли включить 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()?


person Saren Arterius    schedule 08.01.2015    source источник
comment
Я предполагаю, что последнее может быть достигнуто путем проверки нижней части recyclerview против последнего дочернего элемента, то есть что-то в строках mRecyclerView.getBottom()== linearLayoutmanager.findViewbyPosition(adapter.getItemCount() - 1).getBottom()   -  person humblerookie    schedule 24.08.2015
comment
@Saren Arterius, возможно, вы захотите взглянуть на это   -  person Sheraz Ahmad Khilji    schedule 04.11.2015


Ответы (11)


Решение в менеджере компоновки.

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, вам не нужно будет этого делать

person humblerookie    schedule 06.03.2015
comment
Нет, это все еще происходит, когда RecyclerView является прямым потомком SwipeRefreshLayout. Но проверка firstCompletelyVisibleItemPosition решает проблему. Мне нужно указать кое-что здесь. Если ни один из элементов не виден полностью, firstCompletelyVisibleItemPosition() возвращает -1. Таким образом, это решение не будет работать, если первый элемент не помещается в RecyclerView при прокрутке вверх. Но это очень редкая ситуация. - person eluleci; 31.03.2015
comment
@eluleci: проверьте, находится ли список вверху RecyclerView rc=(RecyclerView)view; LinearLayoutManager lm= (LinearLayoutManager) rc.getLayoutManager(); if(lm.findViewByPosition(lm.findFirstVisibleItemPosition()).getTop()==0 && lm.findFirstVisibleItemPosition()==0) вернуть true; // Это сработает, даже если элемент больше размера экрана - person humblerookie; 27.04.2015
comment
Большой! Мои строки достаточно короткие, но я думаю, что это будет полезно для других. Спасибо - person eluleci; 28.04.2015
comment
Это не удастся, если элемент (первый или последний) больше, чем весь экран. Поскольку first/lastCompletelyVisibleItemPosition() вернет -1, подразумевается, что полностью видимого элемента вообще нет. - person Subin Sebastian; 20.08.2015
comment
@SubinSebastian: я так хочу, чтобы люди нашли время, чтобы прочитать полный ответ. В редакции от 1 июня упоминается тот самый случай - person humblerookie; 04.10.2015
comment
У меня работал только lastVisibleItemPosition без Completely - person Hamzeh Soboh; 07.01.2016
comment
И lastVisibleItemPosition, и lastCompletelyVisibleItemPosition не работают. (не удается разрешить метод). Помощь? - person Stardust; 08.07.2016
comment
Здравствуйте, я решил свою проблему - используйте final android.support.v7.widget.LinearLayoutManager layoutManager = new LinearLayoutManager(this); (см. ком/вопросы/29327013/) - person Stardust; 08.07.2016
comment
Также используйте findLastCompletelyVisibleItemPosition. - person Stardust; 08.07.2016
comment
findLastCompletelyVisibleItemPosition будет FAIL, если высота последнего элемента больше высоты экрана. Это означает, что последний элемент виден, но не полностью, поэтому layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION). Используйте lastVisibleItemPosition и проверьте, чтобы нижняя часть последнего вида в макете была безопаснее. - person gone; 29.08.2016
comment
recyclerView.getLayoutManager() не имеет этих методов, потому что он возвращает LayoutManager, а не LinearLayoutManager (который имеет нужный вам метод) - person Dragos Rachieru; 13.03.2018
comment
@DragosRachieru Что мне здесь не хватает, разве это не то, что объяснено в решении с помощью кода? - person humblerookie; 14.03.2018
comment
я объяснял @Stardust, почему такого метода нет - person Dragos Rachieru; 16.03.2018

Вы можете попробовать recyclerView.canScrollVertically(int direction), если вам просто нужно знать, можно ли прокручивать или нет.

Целые числа направления:

  • -1 вверх
  • 1 вниз
  • 0 всегда будет возвращать false.
person Penzzz    schedule 11.06.2015
comment
Однако это должно было бы повторяться с интервалом, верно? - person Stardust; 08.07.2016
comment
Вы можете это сделать в recyclerView.addOnScrollListener.. это работает как шарм... спасибо - person therealprashant; 12.09.2016
comment
Большой! Это возвращает false, когда мы достигаем конца прокрутки, мне нужно это событие. - person box; 25.11.2016
comment
Превосходно. Используйте это в onScrollStateChanged для меня. - person Lym Zoy; 06.02.2017
comment
просто помните: если нет элемента для отображения или элемента недостаточно для прокрутки, он всегда будет возвращать false - person vuhung3990; 09.02.2017
comment
recyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN) == false вы достигли дна - person M. Reza Nasirloo; 13.04.2017
comment
целые числа направления: -1 вверх, 1 вниз, 0 всегда возвращает false. - person Grux; 04.09.2017

Чтобы проверить, прокручивается ли 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;
    }

Некоторая дополнительная информация

Если вы хотите реализовать ScrollToBottom в RecyclerView, когда Edittext равно tapped, я рекомендую добавить 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;
            }
        });
person Sheraz Ahmad Khilji    schedule 04.11.2015
comment
Работал на меня. Мне пришлось добавить его в RecyclerView addOnScrollListener(), хотя - person aphoe; 29.05.2016
comment
это не работает для тривиального случая, когда в recyclerview есть один элемент, и он полностью виден. - person techtinkerer; 22.10.2016
comment
ты сэкономил день - person Reena; 08.02.2017

@Saren Arterius, используя addOnScrollListener в RecycleView, вы можете найти прокручиваемую верхнюю или нижнюю часть вертикального 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...
                }
            }
        });
person TejaDroid    schedule 10.05.2016

Используйте recyclerView.canScrollVertically(int direction), чтобы проверить, достигнута ли верхняя или нижняя часть прокрутки.

direction = 1 для прокрутки вниз (вниз)

direction = -1 для прокрутки вверх (вверх)

если метод возвращает false, это означает, что вы достигли вершины или основания в зависимости от направления.

person kartik srivastava    schedule 09.08.2018
comment
Большое спасибо, это сработало! - person Ferer Atlus; 17.06.2020

Просто сохраните ссылку на свой 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;
            }
        }
    }
});
person SGal    schedule 06.02.2015
comment
setOnScrollListner больше не рекомендуется. - person Shajeel Afzal; 04.09.2015
comment
Вы должны использовать addOnScrollListener, потому что, как сказал @shajeelAfzal, setOnScrollListener теперь устарел, вы должны использовать addOnScrollListener, потому что, как сказал @shajeelAfzal, setOnScrollListener теперь устарел, проверьте здесь - person Vasilisfoo; 08.09.2015
comment
Что такое previousTotal? - person CoolMind; 06.03.2019

Вы можете обнаружить вершину, используя следующие

 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

person Manohar    schedule 11.04.2017
comment
Это кажется неправильным. getChildAt() возвращает первый дочерний элемент ViewGroup, который может (и будет) отличаться от первого элемента списка. Если этот дочерний элемент окажется идеально выровненным с верхней частью recyclerview, ваш метод IsRecyclerViewAtTop() вернет true, даже если он не находится наверху. - person aspyct; 29.05.2017

Я написал 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;
}

}

person chefish    schedule 06.02.2017

ты можешь сделать это, это работа для меня

      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);
            }
        });
person Amal Kronz    schedule 06.02.2017

Чтобы узнать, прокручивается ли RecyclerView вниз, вы можете повторно использовать методы, используемые для полосы прокрутки.

Это вычисление, для удобства написанное как функция расширения Kotlin:

fun RecyclerView.isScrolledToBottom(): Boolean {
    val contentHeight = height - (paddingTop + paddingBottom)
    return computeVerticalScrollRange() == computeVerticalScrollOffset() + contentHeight
}
person Giso Bartels    schedule 04.04.2018
comment
Хорошее решение! Я видел некоторые проблемы, когда размер прокрутки был всего на 1, хотя я прокручивался внизу (вероятно, некоторые проблемы с округлением). Поэтому я изменил код, чтобы использовать поле: fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight >= margin } - person Renso Lohuis; 06.04.2018
comment
Я имею в виду, меньше или равно марже. Вот так: fun RecyclerView.isScrolledToBottom(margin: Int = 1): Boolean { val contentHeight = height - (paddingTop + paddingBottom) return computeVerticalScrollRange() - computeVerticalScrollOffset() - contentHeight <= margin } - person Renso Lohuis; 06.04.2018

вы можете попробовать с 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;
}
person Jch Pal    schedule 08.08.2016