web-dev-qa-db-fra.com

Détecter le début et la fin du défilement dans recyclerview

Je dois détecter le début/la fin et la direction du défilement dans une vue de recyclage. Le listener de défilement a deux méthodes: onScrolled() et onScrollStateChanged(). La première méthode est appelée après le démarrage du défilement (elle est en effet appelée onScrolled () et non onScrolling ()). La deuxième méthode donne des informations sur l'état mais je n'ai pas les informations de direction. Comment puis-je atteindre mon objectif?

27
greywolf82

étape 1 Vous pouvez créer une classe étendant RecyclerView.OnScrollListener et substituer ces méthodes.

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

étape 2 - Puisque setOnScrollListener est obsolète, il est préférable d'utiliser addOnScrollListener 

 mRecyclerView.addOnScrollListener(new CustomScrollListener());
64
bond007

Voir la documentation de onScrollStateChanged(int state) . Les trois valeurs possibles sont:

  • SCROLL_STATE_IDLE: Aucun défilement n'est effectué.
  • SCROLL_STATE_DRAGGING: l'utilisateur fait glisser son doigt sur l'écran (ou le fait est programmé.
  • SCROLL_STATE_SETTLING: l'utilisateur a levé le doigt et l'animation ralentit maintenant.

Donc, si vous voulez détecter quand le défilement commence et se termine, vous pouvez créer quelque chose comme ceci:

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

Le moyen le plus simple de le faire est d'extraire votre layoutManager. Par exemple

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

J'espère que ça aide! 

3
Miljan Rakita

Je pense que les autres réponses ne touchent pas votre douleur.

À propos de la question

Comme vous l'avez dit, RecycleView appellera la méthode onScrollStateChanged () avant la méthode onScrolled (). Cependant, la méthode onScrollStateChanged () ne peut vous indiquer que les trois statuts de RecycleView: 

  • SCROLL_STATE_IDLE
  • SCROLL_STATE_DRAGGING
  • SCROLL_STATE_SETTLING

Cela signifie que, supposons que RecycleView soit à son sommet maintenant, si l'utilisateur fait défiler l'écran vers le haut, il ne peut déclencher que la méthode onScrollStateChanged(), mais pas la méthode onScrolled() .. Et si l'utilisateur fait défiler l'écran vers le bas, il peut déclencher d'abord la méthode onScrollStateChanged(), puis la méthode onScrolled().

Ensuite, le problème vient, SCROLL_STATE_DRAGGING peut seulement vous dire que l'utilisateur fait glisser la vue, pas indiquer la direction de son déplacement.

Vous devez combiner avec la méthode dy de onScrolled() pour détecter le sens du déplacement, mais onScrolled() ne s'est pas déclenché avec onScrollStateChanged().

MySolution:

Enregistrez l'état de défilement lorsque onScrollStateChanged() s'est déclenché, puis indiquez une heure, par exemple 10 ms, vérifiez si le mouvement s'effectue sur l'axe des Y et déterminez la direction du glissement.

Code Kotlin:

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
            }
        }
    }
0
CalvinChe

voici une solution complète pour effectuer une action après l’arrêt du défilement (pour RecyclerView)

qui a corrigé mes exceptions OutOfBounds liées au défilement recyclerView

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