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


81

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

@ Сарен Артериус, возможно, вы захотите взглянуть на это
Шераз Ахмад Хилджи

Ответы:


109

Решение есть в менеджере по расположению.

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

1
@eluleci: Проверить, находится ли список наверху RecyclerView rc = (RecyclerView) view; LinearLayoutManager lm = (LinearLayoutManager) rc.getLayoutManager (); if (lm.findViewByPosition (lm.findFirstVisibleItemPosition ()). getTop () == 0 && lm.findFirstVisibleItemPosition () == 0) return true; // Это сработает даже, если размер элемента больше экрана
humblerookie

2
Это не сработает, если элемент (первый или последний) больше, чем весь экран. Поскольку, first/lastCompletelyVisibleItemPosition()вернет -1, подразумевает, что полностью видимого элемента нет вообще.
Субин Себастьян,

4
Оба так lastVisibleItemPositionи lastCompletelyVisibleItemPositionне работают. (невозможно разрешить метод). Помогите?
Stardust

4
findLastCompletelyVisibleItemPositionбудет НЕОБХОДИМ, если высота последнего элемента больше, чем высота экрана. Это означает, что последний элемент виден, но не полностью layout.findLastCompletelyVisibleItemPosition()=-1(RecyclerView.NO_POSITION). Использование lastVisibleItemPositionи проверка нижней части последнего вида в макете будет безопаснее.
ушел

78

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

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

  • -1 за вверх
  • 1 за пух
  • 0 всегда будет возвращать false.

Но ведь это нужно повторять с интервалом, верно?
Stardust

1
Вы можете использовать это в recyclerView.addOnScrollListener .. это работает как шарм ... спасибо
therealprashant

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

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

Если вы хотите реализовать 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;
            }
        });

Работал у меня. Я должен был добавить его в RecyclerView в addOnScrollListener () , хотя
aphoe

1
это не работает для тривиального случая, когда в recyclerview есть один элемент, и он полностью виден.
techtinkerer

Вы спасли день
Рина

22

@Saren Артериус, Использование 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...
                }
            }
        });

6

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

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

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

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


Большое спасибо, это сработало!
Ferer Atlus

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устарел.
Шажил Афзал

2
Вы должны использовать, addOnScrollListenerпотому что, как сказал @shajeelAfzal, setOnScrollListenerсейчас устарел, вы должны использовать, addOnScrollListenerпотому что, как сказал @shajeelAfzal, setOnScrollListenerсейчас устарел, проверьте здесь
Vasilisfoo

Что есть previousTotal?
CoolMind 06

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 }
Ренсо Лохус

Я имею в виду, меньше или равно марже. Как это: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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.