Обнаружение начала прокрутки и завершения прокрутки в режиме recyclerview

Мне нужно определить начало/конец и направление прокрутки в режиме recyclerview. У слушателя прокрутки есть два метода: onScrolled() и onScrollStateChanged(). Первый метод вызывается после запуска свитка (на самом деле он называется onScrolled(), а не onScrolling()). Второй способ дает информацию о состоянии, но у меня нет информации о направлении. Как я могу достичь своей цели?

Ответы

Ответ 1

шаг 1 Вы можете создать класс, расширяющий RecyclerView.OnScrollListener и переопределить эти методы

public class CustomScrollListener extends RecyclerView.OnScrollListener {
    public CustomScrollListener() {
    }

    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        switch (newState) {
            case RecyclerView.SCROLL_STATE_IDLE:
                System.out.println("The RecyclerView is not scrolling");
                break;
            case RecyclerView.SCROLL_STATE_DRAGGING:
                System.out.println("Scrolling now");
                break;
            case RecyclerView.SCROLL_STATE_SETTLING:
                System.out.println("Scroll Settling");
                break;

        }

    }

    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (dx > 0) {
            System.out.println("Scrolled Right");
        } else if (dx < 0) {
            System.out.println("Scrolled Left");
        } else {
            System.out.println("No Horizontal Scrolled");
        }

        if (dy > 0) {
            System.out.println("Scrolled Downwards");
        } else if (dy < 0) {
            System.out.println("Scrolled Upwards");
        } else {
            System.out.println("No Vertical Scrolled");
        }
    }
}

step 2- Поскольку setOnScrollListener устарел, лучше использовать addOnScrollListener

 mRecyclerView.addOnScrollListener(new CustomScrollListener());

Ответ 2

Смотрите документацию для onScrollStateChanged(int state). Три возможных значения:

  • SCROLL_STATE_IDLE: прокрутка не выполняется.
  • SCROLL_STATE_DRAGGING: пользователь перетаскивает палец по экрану (или выполняется программно).
  • SCROLL_STATE_SETTLING: Пользователь поднял палец, и анимация теперь замедляется.

Итак, если вы хотите определить, когда прокрутка начинается и заканчивается, вы можете создать что-то вроде этого:

public void onScrollStateChanged(int state) {
    boolean hasStarted = state == SCROLL_STATE_DRAGGING;
    boolean hasEnded = state == SCROLL_STATE_IDLE;
}

Ответ 3

Самый простой способ сделать это - вытащить ваш layoutManager. Например

   private RecyclerView.OnScrollListener mListener = new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        GridLayoutManager layoutManager=GridLayoutManager.class.cast(recyclerView.getLayoutManager());
        int visibleItemCount = layoutManager.getChildCount();
        int totalItemCount = layoutManager.getItemCount();
        int pastVisibleItems = layoutManager.findFirstCompletelyVisibleItemPosition();

        if(pastVisibleItems+visibleItemCount >= totalItemCount){
            // End of the list is here.
            Log.i(TAG, "End of list");
        }
    }
}

Надеюсь, это поможет!

Ответ 4

здесь представлено полное решение для выполнения действия после остановки прокрутки (для RecyclerView)

который исправил мой Exception OutOfBounds, связанный с прокруткой recyclerView

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        //Your action here
                    }
                });

Ответ 5

Я думаю, что другие ответы не касаются вашей боли.

О вопросе

Как вы сказали, RecycleView вызовет onScrollStateChanged() перед методом onScrolled(). Однако метод onScrollStateChanged() может сообщить вам только три состояния RecycleView:

  • SCROLL_STATE_IDLE
  • SCROLL_STATE_DRAGGING
  • SCROLL_STATE_SETTLING

Это означает, что предположим, что RecycleView сейчас на вершине, если пользователь прокручивает вверх, он может запускать onScrollStateChanged() метод onScrollStateChanged(), но не onScrolled(). И если пользователь прокручивает onScrollStateChanged() вниз, он может onScrollStateChanged() вызвать onScrollStateChanged(), а затем onScrolled().

Тогда возникает проблема, SCROLL_STATE_DRAGGING может только сказать вам, что пользователь перетаскивает представление, а не SCROLL_STATE_DRAGGING направление его перетаскивания.

Вы должны комбинировать с методом dy из onScrolled() чтобы определить направление перетаскивания, но onScrolled() не запускается с onScrollStateChanged().

MySolution:

Запишите состояние прокрутки при onScrollStateChanged(), затем определите время, например, 10 мс, проверьте, есть ли движение по оси Y, и определите направление перетаскивания.

Код Котлина:

class DraggingDirectionScrollListener(private val view: View)
        : RecyclerView.OnScrollListener() {
        private val DIRECTION_NONE = -1
        private val DIRECTION_UP = 0
        private val DIRECTION_DOWN = 1

        var scrollDirection = DIRECTION_NONE
        var listStatus = RecyclerView.SCROLL_STATE_IDLE
        var totalDy = 0

        override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
            listStatus = newState

            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                scrollDirection = DIRECTION_NONE
            }

            if (isOnTop() && newState == RecyclerView.SCROLL_STATE_DRAGGING) {
              view.postDelayed({
                    if (getDragDirection() == DIRECTION_DOWN){
                        // Drag down from top
                    }else if (getDragDirection() == DIRECTION_UP) {
                        // Drag Up from top
                    }
                }, 10)
            }
        }

        override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
            this.totalDy += dy
            scrollDirection = when {
                dy > 0 -> DIRECTION_UP
                dy < 0 -> DIRECTION_DOWN
                else -> DIRECTION_NONE
            }

        }

        /**
         * is on the top of the list
         */
        private fun isOnTop():Boolean{
            return totalDy == 0
        }

        /**
         * detect dragging direction
         */
        private fun getDragDirection():Int {
            if (listStatus != RecyclerView.SCROLL_STATE_DRAGGING) {
                return DIRECTION_NONE
            }

            return when (scrollDirection) {
                DIRECTION_NONE -> if (totalDy == 0){
                    DIRECTION_DOWN  // drag down from top
                }else{
                    DIRECTION_UP  // drag up from bottom
                }
                DIRECTION_UP -> DIRECTION_UP
                DIRECTION_DOWN -> DIRECTION_DOWN
                else -> DIRECTION_NONE
            }
        }
    }

Ответ 6

Используйте этот код для избежания повторных звонков

используйте -1 для определения вершины

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

            if (!recyclerView.canScrollVertically(1) && newState==RecyclerView.SCROLL_STATE_IDLE) {
                Log.d("-----","end");

            }
        }
    });