web-dev-qa-db-fra.com

Détecter le défilement vers le haut et le défilement vers le bas dans ListView

J'ai l'exigence suivante:

  • Au début, les données de la page n ° 2 sont extraites du serveur et les éléments sont renseignés dans un ListView.

Considérant que la page précédente et la page suivante sont disponibles dans un scénario, le code suivant a été ajouté:

 if(prevPageNo > 0){
    mListViewActual.setOnScrollListener(this);
 }

 if(nextPageNo > 0){
    mListViewActual.setOnScrollListener(this);
 }

Quelles conditions dois-je mettre pour détecter le défilement vers le haut et le défilement vers le bas sur les méthodes suivantes:

  1. void onScroll (vue AbsListView, int premièreVisibleItem, int visibleItemCount, int totalItemCount)
  2. void onScrollStateChanged (vue AbsListView, int scrollState)

Après l'action: détection de défilement vers le haut et vers le bas, un service sera alors appelé avec soit la page précédente no, soit la page suivante no, pour extraire les éléments à renseigner dans la vue liste.

Toute entrée sera utile.

Passé par les liens suivants mais ne renvoyant pas la bonne action de défilement vers le haut/défilement:

lien 1lien 2

68
chiranjib

essayez d'utiliser setOnScrollListener et implémentez onScrollStateChanged avec scrollState.

setOnScrollListener(new OnScrollListener(){
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      // TODO Auto-generated method stub
    }
    public void onScrollStateChanged(AbsListView view, int scrollState) {
      // TODO Auto-generated method stub
      final ListView lw = getListView();

       if(scrollState == 0) 
      Log.i("a", "scrolling stopped...");


        if (view.getId() == lw.getId()) {
        final int currentFirstVisibleItem = lw.getFirstVisiblePosition();
         if (currentFirstVisibleItem > mLastFirstVisibleItem) {
            mIsScrollingUp = false;
            Log.i("a", "scrolling down...");
        } else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
            mIsScrollingUp = true;
            Log.i("a", "scrolling up...");
        }

        mLastFirstVisibleItem = currentFirstVisibleItem;
    } 
    }
  });
90
Sunil Kumar

Voici une version de travail modifiée de certaines des solutions indiquées ci-dessus.

Ajouter une autre classe ListView:

package com.example.view;

import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.View;
import Android.widget.AbsListView;

public class ListView extends Android.widget.ListView {

    private OnScrollListener onScrollListener;
    private OnDetectScrollListener onDetectScrollListener;

    public ListView(Context context) {
        super(context);
        onCreate(context, null, null);
    }

    public ListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        onCreate(context, attrs, null);
    }

    public ListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        onCreate(context, attrs, defStyle);
    }

    @SuppressWarnings("UnusedParameters")
    private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
        setListeners();
    }

    private void setListeners() {
        super.setOnScrollListener(new OnScrollListener() {

            private int oldTop;
            private int oldFirstVisibleItem;

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (onScrollListener != null) {
                    onScrollListener.onScrollStateChanged(view, scrollState);
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (onScrollListener != null) {
                    onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
                }

                if (onDetectScrollListener != null) {
                    onDetectedListScroll(view, firstVisibleItem);
                }
            }

            private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
                View view = absListView.getChildAt(0);
                int top = (view == null) ? 0 : view.getTop();

                if (firstVisibleItem == oldFirstVisibleItem) {
                    if (top > oldTop) {
                        onDetectScrollListener.onUpScrolling();
                    } else if (top < oldTop) {
                        onDetectScrollListener.onDownScrolling();
                    }
                } else {
                    if (firstVisibleItem < oldFirstVisibleItem) {
                        onDetectScrollListener.onUpScrolling();
                    } else {
                        onDetectScrollListener.onDownScrolling();
                    }
                }

                oldTop = top;
                oldFirstVisibleItem = firstVisibleItem;
            }
        });
    }

    @Override
    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }

    public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
        this.onDetectScrollListener = onDetectScrollListener;
    }
}

Et une interface:

public interface OnDetectScrollListener {

    void onUpScrolling();

    void onDownScrolling();
}

Et enfin comment utiliser:

com.example.view.ListView listView = (com.example.view.ListView) findViewById(R.id.list);
listView.setOnDetectScrollListener(new OnDetectScrollListener() {
    @Override
    public void onUpScrolling() {
        /* do something */
    }

    @Override
    public void onDownScrolling() {
        /* do something */
    }
});

Dans votre mise en page XML:

<com.example.view.ListView
    Android:id="@+id/list"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"/>

Ceci est mon premier sujet, ne me jugez pas sévèrement. =)

63
maXp

c'est une implémentation simple:

lv.setOnScrollListener(new OnScrollListener() {
        private int mLastFirstVisibleItem;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {

            if(mLastFirstVisibleItem<firstVisibleItem)
            {
                Log.i("SCROLLING DOWN","TRUE");
            }
            if(mLastFirstVisibleItem>firstVisibleItem)
            {
                Log.i("SCROLLING UP","TRUE");
            }
            mLastFirstVisibleItem=firstVisibleItem;

        }
    });

et si vous avez besoin de plus de précision, vous pouvez utiliser cette classe ListView personnalisée:

import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.View;
import Android.widget.AbsListView;
import Android.widget.ListView;

/**
 * Created by root on 26/05/15.
 */
public class ScrollInterfacedListView extends ListView {


    private OnScrollListener onScrollListener;
    private OnDetectScrollListener onDetectScrollListener;

    public ScrollInterfacedListView(Context context) {
        super(context);
        onCreate(context, null, null);
    }

    public ScrollInterfacedListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        onCreate(context, attrs, null);
    }

    public ScrollInterfacedListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        onCreate(context, attrs, defStyle);
    }

    @SuppressWarnings("UnusedParameters")
    private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
        setListeners();
    }

    private void setListeners() {
        super.setOnScrollListener(new OnScrollListener() {

            private int oldTop;
            private int oldFirstVisibleItem;

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (onScrollListener != null) {
                    onScrollListener.onScrollStateChanged(view, scrollState);
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (onScrollListener != null) {
                    onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
                }

                if (onDetectScrollListener != null) {
                    onDetectedListScroll(view, firstVisibleItem);
                }
            }

            private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
                View view = absListView.getChildAt(0);
                int top = (view == null) ? 0 : view.getTop();

                if (firstVisibleItem == oldFirstVisibleItem) {
                    if (top > oldTop) {
                        onDetectScrollListener.onUpScrolling();
                    } else if (top < oldTop) {
                        onDetectScrollListener.onDownScrolling();
                    }
                } else {
                    if (firstVisibleItem < oldFirstVisibleItem) {
                        onDetectScrollListener.onUpScrolling();
                    } else {
                        onDetectScrollListener.onDownScrolling();
                    }
                }

                oldTop = top;
                oldFirstVisibleItem = firstVisibleItem;
            }
        });
    }

    @Override
    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }

    public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
        this.onDetectScrollListener = onDetectScrollListener;
    }


    public interface OnDetectScrollListener {

        void onUpScrolling();

        void onDownScrolling();
    }

}

un exemple d'utilisation: (n'oubliez pas de l'ajouter en tant que balise Xml dans votre layout.xml)

scrollInterfacedListView.setOnDetectScrollListener(new ScrollInterfacedListView.OnDetectScrollListener() {
            @Override
            public void onUpScrolling() {
               //Do your thing
            }

            @Override
            public void onDownScrolling() {

             //Do your thing
            }
        });
44
Gal Rom

Avec toute la méthode publiée, il y a des problèmes pour reconnaître quand l'utilisateur fait défiler du premier élément ou en bas du dernier. Voici une autre approche pour détecter le défilement vers le haut/bas:

        listView.setOnTouchListener(new View.OnTouchListener() {
            float height;
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                float height = event.getY();
                if(action == MotionEvent.ACTION_DOWN){
                    this.height = height;
                }else if(action == MotionEvent.ACTION_UP){
                    if(this.height < height){
                        Log.v(TAG, "Scrolled up");
                    }else if(this.height > height){
                        Log.v(TAG, "Scrolled down");
                    }
                }
                return false;
            }
        });
10
Javier Carmona
            ListView listView = getListView();
            listView.setOnScrollListener(new OnScrollListener() {

                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {

                    view.setOnTouchListener(new OnTouchListener() {
                        private float mInitialX;
                        private float mInitialY;

                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                            switch (event.getAction()) {
                                case MotionEvent.ACTION_DOWN:
                                    mInitialX = event.getX();
                                    mInitialY = event.getY();
                                    return true;
                                case MotionEvent.ACTION_MOVE:
                                    final float x = event.getX();
                                    final float y = event.getY();
                                    final float yDiff = y - mInitialY;
                                    if (yDiff > 0.0) {
                                        Log.d(tag, "SCROLL DOWN");
                                        scrollDown = true;
                                        break;

                                    } else if (yDiff < 0.0) {
                                        Log.d(tag, "SCROLL up");
                                        scrollDown = true;
                                        break;

                                    }
                                    break;
                            }
                            return false;
                        }
                    });
10
user1467024

Ma solution fonctionne parfaitement en donnant la valeur exacte pour chaque direction de défilement. distanceFromFirstCellToTop contient la distance exacte entre la première cellule et le haut de la vue parent. Je sauvegarde cette valeur dans previousDistanceFromFirstCellToTop et, lors du défilement, je la compare à la nouvelle valeur. Si c'est plus bas que j'ai défilé, sinon, je l'ai fait défiler.

private int previousDistanceFromFirstCellToTop;

listview.setOnScrollListener(new OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        View firstCell = listview.getChildAt(0);
        int distanceFromFirstCellToTop = listview.getFirstVisiblePosition() * firstCell.getHeight() - firstCell.getTop();

        if(distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
        {
           //Scroll Up
        }
        else if(distanceFromFirstCellToTop  > previousDistanceFromFirstCellToTop)
        {
           //Scroll Down
        }
        previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
    }
});

Pour les développeurs Xamarin, la solution est la suivante:

Note: N'oubliez pas de courir sur le fil de l'interface utilisateur

listView.Scroll += (o, e) =>
{
    View firstCell = listView.GetChildAt(0);
    int distanceFromFirstCellToTop = listView.FirstVisiblePosition * firstCell.Height - firstCell.Top;

    if (distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
    {
        //Scroll Up
    }
    else if (distanceFromFirstCellToTop > previousDistanceFromFirstCellToTop)
    {
        //Scroll Down
    }
    previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
};
8
da Rocha Pires

Pour détecter également le défilement d'éléments plus volumineux, je préfère un écouteur onTouch:

listview.setOnTouchListener(new View.OnTouchListener() {

        int scrollEventListSize = 5; 
        float lastY;
        // Used to correct for occasions when user scrolls down(/up) but the onTouchListener detects it incorrectly. We will store detected up-/down-scrolls with -1/1 in this list and evaluate later which occured more often
        List<Integer> downScrolledEventsHappened;

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            float diff = 0;
            if(event.getAction() == event.ACTION_DOWN){
                lastY = event.getY();
                downScrolledEventsHappened = new LinkedList<Integer>();
            }
            else if(event.getAction() == event.ACTION_MOVE){
                diff = event.getY() - lastY;
                lastY = event.getY();

                if(diff>0)
                    downScrolledEventsHappened.add(1);
                else 
                    downScrolledEventsHappened.add(-1);

               //List needs to be filled with some events, will happen very quickly
                if(downScrolledEventsHappened.size() == scrollEventListSize+1){
                    downScrolledEventsHappened.remove(0);
                    int res=0;
                    for(int i=0; i<downScrolledEventsHappened.size(); i++){
                        res+=downScrolledEventsHappened.get(i);
                    }

                    if (res > 0) 
                        Log.i("INFO", "Scrolled up");
                    else 
                        Log.i("INFO", "Scrolled down");
                }
            }
            return false; // don't interrupt the event-chain
        }
    });
4
AljoSt

J'ai utilisé cette solution beaucoup plus simple:

setOnScrollListener( new OnScrollListener() 
{

    private int mInitialScroll = 0;

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) 
    {
        int scrolledOffset = computeVerticalScrollOffset();

        boolean scrollUp = scrolledOffset > mInitialScroll;
        mInitialScroll = scrolledOffset;
    }


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {


    }

}
4
Vaiden

Il suffit de définir l’auditeur de défilement à votre liste.

Si vous avez un en-tête ou un pied de page, vous devez également vérifier le nombre visible. Si cela augmente, cela signifie que vous faites défiler vers le bas. (Inversez-le s'il y a un pied de page au lieu de l'en-tête)

Si vous n'avez pas d'en-tête ou de pied de page dans votre liste, vous pouvez supprimer les lignes indiquant le nombre d'éléments visibles.

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (mLastFirstVisibleItem > firstVisibleItem) {
                Log.e(getClass().toString(), "scrolling up");
            } else if (mLastFirstVisibleItem < firstVisibleItem) {
                Log.e(getClass().toString(), "scrolling down");
            } else if (mLastVisibleItemCount < visibleItemCount) {
                Log.e(getClass().toString(), "scrolling down");
            } else if (mLastVisibleItemCount > visibleItemCount) {
                Log.e(getClass().toString(), "scrolling up");
            }
            mLastFirstVisibleItem = firstVisibleItem;
            mLastVisibleItemCount = visibleItemCount;
        }

        public void onScrollStateChanged(AbsListView listView, int scrollState) {
        }
    });

et ont ces variables

int mLastFirstVisibleItem;
int mLastVisibleItemCount;
3
eluleci

Stockez le premier objet visible et sur le prochain onScroll, vérifiez si le nouvel objet premierVisibleItem est plus petit ou plus grand que le précédent.

Exemple de pseudocode (non testé):

int lastVisibleItem = 0;
boolean isScrollingDown = false;

void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (firstVisibleItem > lastVisibleItem) {
        isScrollingDown = true;
    }
    else {
        isScrollingDown = false;
    }
    lastVisibleItem = firstVisibleItem;
}
3
thiagolr

Pour une raison quelconque, le Android doc ne couvre pas cela, et la méthode utilisée n’est même pas dans la documentation ... m’a pris un certain temps pour le trouver.

Vous utiliserez ceci pour détecter si votre parchemin est en haut.

public boolean checkAtTop() 
{
    if(listView.getChildCount() == 0) return true;
    return listView.getChildAt(0).getTop() == 0;
}

Cela permettra de vérifier si votre scroller est au sommet. Maintenant, afin de le faire pour le bas, vous devez indiquer le nombre d’enfants que vous avez et vérifier par rapport à ce nombre. Vous devrez peut-être déterminer combien il y a à l'écran en même temps et soustraire cela de votre nombre d'enfants. Je n'ai jamais eu à faire ça. J'espère que cela t'aides

1
Shaun

Un moyen simple de détecter le défilement vers le haut/bas sur Android listview

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
  if(prevVisibleItem != firstVisibleItem){
    if(prevVisibleItem < firstVisibleItem)
      //ScrollDown
    else
      //ScrollUp

  prevVisibleItem = firstVisibleItem;
}

n'oublie pas

yourListView.setOnScrollListener(yourScrollListener);
1
Rick Royd Aban

Astuce pour détecter défilement vers le haut ou le bas dans listview, vous appelez simplement cette fonction sur la fonction onScroll dans OnScrollListener de ListView.

private int oldFirstVisibleItem = -1;
    private protected int oldTop = -1;
    // you can change this value (pixel)
    private static final int MAX_SCROLL_DIFF = 5;

    private void calculateListScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (firstVisibleItem == oldFirstVisibleItem) {
            int top = view.getChildAt(0).getTop();
            // range between new top and old top must greater than MAX_SCROLL_DIFF
            if (top > oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
                // scroll up
            } else if (top < oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
                // scroll down
            }
            oldTop = top;
        } else {
            View child = view.getChildAt(0);
            if (child != null) {
                oldFirstVisibleItem = firstVisibleItem;
                oldTop = child.getTop();
            }
        }
    }
1
thanhlcm

J'ai rencontré des problèmes en utilisant des exemples où la taille de cellule de ListView est excellente. J'ai donc trouvé une solution à mon problème qui détecte le moindre mouvement de votre doigt. J'ai simplifié au minimum possible et se présente comme suit:

private int oldScrolly;


@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int    visibleItemCount, int totalItemCount) {

            View view = absListView.getChildAt(0);
            int scrolly = (view == null) ? 0 : -view.getTop() + absListView.getFirstVisiblePosition() * view.getHeight();
            int margin = 10;

            Log.e(TAG, "Scroll y: " + scrolly + " - Item: " + firstVisibleItem);


            if (scrolly > oldScrolly + margin) {
                Log.d(TAG, "SCROLL_UP");
                oldScrolly = scrolly;
            } else if (scrolly < oldScrolly - margin) {
                Log.d(TAG, "SCROLL_DOWN");
                oldScrolly = scrolly;
            }
        }
    });

PD: J'utilise la marge pour ne pas détecter le défilement jusqu'à ce que vous atteigniez cette marge. Cela évite les problèmes lorsque je montre ou masque les vues et évite de les clignoter.

0

Voici ce que je voudrais essayer en premier:

1) Créez une interface (appelons-la OnScrollTopOrBottomListener) avec ces méthodes:

void onScrollTop ();

vide onScrollBottom ();

2) Dans l'adaptateur de votre liste, ajoutez une instance de membre, qui correspond à l'interface que vous avez créée, et fournissez un séparateur et un getter.

3) Dans l'implémentation getView () de votre adaptateur, vérifiez si le paramètre position est 0 ou getCount () - 1. Vérifiez également que votre instance OnScrollTopOrBottomListener n'est pas null.

4) Si la position est 0, appelez onScrollTopOrBottomListener.onScrollTop (). Si position est getCount () - 1, appelez onScrollTopOrBottomListener.onScrollBottom ().

5) Dans votre implémentation OnScrollTopOrBottomListener, appelez les méthodes appropriées pour obtenir les données souhaitées.

J'espère que cela contribue de quelque façon.

-Brandon

0
Brandon