Як дізнатись, чи прокручується 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()?


Я припускаю, що останнього можна досягти, перевіривши нижню частину рециркуляційного огляду щодо останньої дочірньої організації, тобто чогось у рядках mRecyclerView.getBottom () == linearLayoutmanager.findViewbyPosition (adapter.getItemCount () - 1) .getBottom ()
humblerookie

@Saren Arterius, можливо, ти захочеш поглянути на це
Sheraz Ahmad Khilji

Відповіді:


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

Працював у мене. Мені довелося додати його до addOnScrollListener () RecyclerView, хоча
афо

1
це не спрацьовує для тривіального випадку, коли в перегляді утилізатора є один предмет, і він повністю видно.
techtinkerer

ви врятували день
Reena

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.canScrollVertical (int direction) щоб перевірити, чи досягнуто верх чи низ прокрутки.

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

напрямок = -1 для прокрутки вгору (зверху)

якщо метод повертає false, це означає, що ви досягли верху або низу, залежить від напрямку.


Велике спасибі, це спрацювало!
Ферер Атлус

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

Що це таке 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;
    }

Це виявить верх, коли ви відпустите палець, коли досягли вершини, якщо ви хочете виявити, як тільки реациклерпогляд досягне верху перевірка if(IsRecyclerViewAtTop())всередині onScrolled методу


3
Здається, це неправильно. getChildAt()повертає перший дочірній ViewGroupелемент, який може (і буде) відрізнятися від першого елемента списку. Якщо ця дитина виявиться ідеально вирівняною до верхньої частини перегляду утилізатора, ваш IsRecyclerViewAtTop()метод поверне істину, навіть якщо він не знаходиться вгорі.
aspyct

2

Я написав RecyclerViewHelper, щоб знати, що рециркулятор знаходиться вгорі або внизу.

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 }
Ось так

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.