Puis-je avoir onScrollListener pour un ScrollView?

140

J'utilise un HorizontalScrollViewdans une mise en page et je dois identifier que l'utilisateur a atteint le point de début et de fin du défilement.

Car ListViewj'ai essayé un the onScrollListeneret il est possible de trouver le point de début et de fin du scroll.

J'ai essayé de faire la même chose dans mon Scrollviewmais cela ne semble pas possible. Existe-t-il d'autres moyens possibles de réaliser ce dont j'ai besoin.

arnp
la source
3
C'est possible. Voir la réponse de user2695685. En bref, ce qui suit onStartfera l'affaire: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});dans onStart () où hsvest un HorizontalScrollViewfonctionne.
JohnnyLambada
accepter n'importe qui de réponse utile..si autre poste votre propre réponse ..
Ranjith Kumar
12
Pourquoi la détection d'un événement de défilement avec un ScrollView est-elle si difficile sous Android? C'est fou imo.
travaillé le

Réponses:

389

Chaque instance d'appels View getViewTreeObserver(). Maintenant, lorsque vous tenez une instance de ViewTreeObserver, vous pouvez y ajouter un OnScrollChangedListener()à l'aide de la méthode addOnScrollChangedListener().

Vous pouvez voir plus d'informations sur cette classe ici .

Il vous permet d'être au courant de chaque événement de défilement - mais sans les coordonnées. Vous pouvez les obtenir en utilisant getScrollY()ou à getScrollX()partir de l'auditeur.

scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        int scrollY = rootScrollView.getScrollY(); // For ScrollView
        int scrollX = rootScrollView.getScrollX(); // For HorizontalScrollView
        // DO SOMETHING WITH THE SCROLL COORDINATES
    }
});
Zbun
la source
6
Cette réponse doit être marquée correcte. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});dans onStart () où hsvest un HorizontalScrollViewfonctionne. Je soupçonne que la même chose fonctionnera pour un ScrollView.
JohnnyLambada
3
exactement. C'est la meilleure réponse. pas besoin d'étendre HorizontalScrollView. Juste testé avec un ScrollView, fonctionne très bien: final ScrollView sv = (ScrollView) findViewById (R.id.scrollViewArticle); sv.getViewTreeObserver (). addOnScrollChangedListener (new ViewTreeObserver.OnScrollChangedListener () {@Override public void onScrollChanged () {Log.d ("onScrollChanged Y", String.valueOf (sv.getScrollY)})
Raphael C
5
Vous devriez d'abord vérifier ViewTreeObserver.isAlive ()
M.Salomaa
7
L'exemple de code dans la réponse introduit une fuite de mémoire. Puisque la méthode est addet non set, tous les écouteurs seront conservés jusqu'à ce qu'ils soient explicitement supprimés. Ainsi, la classe anonyme utilisée comme implémentation d'écouteur dans l'exemple fuira (avec tout ce qu'elle fait référence, c'est-à-dire la classe externe).
Brett Duncavage
7
Probablement une question stupide, mais qu'est-ce que rootScrollView?
Iqbal
49

Cela pourrait être très utile. Utilisez à la NestedScrollViewplace de ScrollView. Support Library 23.1 a introduit un OnScrollChangeListenerto NestedScrollView. Vous pouvez donc faire quelque chose comme ça.

 myScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
            Log.d("ScrollView","scrollX_"+scrollX+"_scrollY_"+scrollY+"_oldScrollX_"+oldScrollX+"_oldScrollY_"+oldScrollY);
            //Do something
        }
    });
Midhun Vijayakumar
la source
2
Certainement plus facile que de savoir si vous avez déjà ajouté l'écouteur avec getViewTreeObserver (). Merci!
Anthony Mills
Mais comment utiliser NestedScrollView comme vue de défilement horizontal? Impossible de trouver une ressource
GensaGames
Et si je veux utiliser le défilement horizontal?
Nikita Axyonov
7
@ dazed'n'confused NestedScrollViewfait partie de la bibliothèque de support, sa setOnScrollChangeListenerméthode n'a pas d'exigence de version minimale.
Tom le
4
À ne pas confondre avec View« s setOnScrollChangeListenerqui requiert un niveau API 23
Anders Ullnæss
18

Voici un HorizontalScrollView dérivé que j'ai écrit pour gérer les notifications sur le défilement et la fin du défilement. Il gère correctement lorsqu'un utilisateur a arrêté le défilement actif et lorsqu'il ralentit complètement après qu'un utilisateur lâche prise:

public class ObservableHorizontalScrollView extends HorizontalScrollView {
    public interface OnScrollListener {
        public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
        public void onEndScroll(ObservableHorizontalScrollView scrollView);
    }

    private boolean mIsScrolling;
    private boolean mIsTouching;
    private Runnable mScrollingRunnable;
    private OnScrollListener mOnScrollListener;

    public ObservableHorizontalScrollView(Context context) {
        this(context, null, 0);
    }

    public ObservableHorizontalScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();

        if (action == MotionEvent.ACTION_MOVE) {
            mIsTouching = true;
            mIsScrolling = true;
        } else if (action == MotionEvent.ACTION_UP) {
            if (mIsTouching && !mIsScrolling) {
                if (mOnScrollListener != null) {
                    mOnScrollListener.onEndScroll(this);
                }
            }

            mIsTouching = false;
        }

        return super.onTouchEvent(ev);
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);

        if (Math.abs(oldX - x) > 0) {
            if (mScrollingRunnable != null) {
                removeCallbacks(mScrollingRunnable);
            }

            mScrollingRunnable = new Runnable() {
                public void run() {
                    if (mIsScrolling && !mIsTouching) {
                        if (mOnScrollListener != null) {
                            mOnScrollListener.onEndScroll(ObservableHorizontalScrollView.this);
                        }
                    }

                    mIsScrolling = false;
                    mScrollingRunnable = null;
                }
            };

            postDelayed(mScrollingRunnable, 200);
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollChanged(this, x, y, oldX, oldY);
        }
    }

    public OnScrollListener getOnScrollListener() {
        return mOnScrollListener;
    }

    public void setOnScrollListener(OnScrollListener mOnEndScrollListener) {
        this.mOnScrollListener = mOnEndScrollListener;
    }

}
ZaBlanc
la source
1
Il semble que ce soit mieux que d'utiliser la méthode ViewTreeObserver. Tout simplement parce que lorsque vous avez une activité où vous avez plusieurs fragments chargés (par exemple 3 fragments avec des onglets coulissants) avec ListViews et ScrollViews et que vous devez enregistrer des événements pour une vue spécifique. Alors que si le ViewTreeObserver est enregistré, il déclenchera des événements même s'il n'est pas une vue active.
Torsten Ojaperv
3
@ZaBlanc - Pouvez-vous s'il vous plaît me dire comment initialiser cette classe et les auditeurs de mon activité, qui contient le ScrollView? Je l'ai très bien implémenté, mais les écouteurs ne renverront pas encore de valeurs.
Edmond Tamas
Cela fonctionne réellement! et pas besoin de sdk> 23 pour ça :)
Matan Dahan
5

Si vous souhaitez connaître la position de défilement d'une vue, vous pouvez utiliser la fonction d'extension suivante sur la classe View:

fun View?.onScroll(callback: (x: Int, y: Int) -> Unit) {
    var oldX = 0
    var oldY = 0
    this?.viewTreeObserver?.addOnScrollChangedListener {
        if (oldX != scrollX || oldY != scrollY) {
            callback(scrollX, scrollY)
            oldX = scrollX
            oldY = scrollY
        }
    }
}
mcspayne
la source
5

Vous pouvez utiliser à la NestedScrollViewplace de ScrollView. Cependant, lors de l'utilisation d'un Kotlin Lambda, il ne saura pas que vous voulez NestedScrollView au setOnScrollChangeListenerlieu de celui de View (qui est le niveau d'API 23). Vous pouvez résoudre ce problème en spécifiant le premier paramètre en tant que NestedScrollView.

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}
Cristan
la source
Cela devrait être la réponse acceptée car la méthode utilisant un viewTreeObserver provoque des fuites de mémoire et doit être supprimée manuellement, sinon plusieurs observateurs de défilement seront ajoutés.
Ray Li
1

vous pouvez définir une classe ScrollView personnalisée et ajouter une interface à appeler lors du défilement comme ceci:

public class ScrollChangeListenerScrollView extends HorizontalScrollView {


private MyScrollListener mMyScrollListener;

public ScrollChangeListenerScrollView(Context context) {
    super(context);
}

public ScrollChangeListenerScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ScrollChangeListenerScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


public void setOnMyScrollListener(MyScrollListener myScrollListener){
    this.mMyScrollListener = myScrollListener;
}


@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if(mMyScrollListener!=null){
        mMyScrollListener.onScrollChange(this,l,t,oldl,oldt);
    }

}

public interface MyScrollListener {
    void onScrollChange(View view,int scrollX,int scrollY,int oldScrollX, int oldScrollY);
}

}
Chuanxing Zhao
la source
0
    // --------Start Scroll Bar Slide--------
    final HorizontalScrollView xHorizontalScrollViewHeader = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewHeader);
    final HorizontalScrollView xHorizontalScrollViewData = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewData);
    xHorizontalScrollViewData.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            int scrollX; int scrollY;
            scrollX=xHorizontalScrollViewData.getScrollX();
            scrollY=xHorizontalScrollViewData.getScrollY();
            xHorizontalScrollViewHeader.scrollTo(scrollX, scrollY);
        }
    });
    // ---------End Scroll Bar Slide---------
Wirote W.
la source
Veuillez essayer d'ajouter une description pour soutenir votre code qui fera comprendre à tout le monde la pâte
Aniruddha Das
Ce code est très similaire à une autre réponse , n'est-ce pas?
Sergii