Posso avere onScrollListener per uno ScrollView?


140

Sto usando a HorizontalScrollViewin un layout e ho bisogno di identificare l'utente ha raggiunto il punto iniziale e finale della pergamena.

Perché ListViewho provato un onScrollListenere è possibile trovare il punto iniziale e finale di scorrimento.

Ho provato a fare lo stesso nel mio Scrollviewma non sembra possibile. Esistono altri modi possibili per ottenere ciò di cui ho bisogno?


3
È possibile. Vedi la risposta dell'utente2695685. In breve, il seguente in onStartfarà il trucco: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});in onStart () dove hsvè un HorizontalScrollViewfunziona.
JohnnyLambada,

accetta qualcuno di utile risposta..se pubblica la tua risposta ..
Ranjith Kumar,

12
Perché rilevare un evento di scorrimento con ScrollView è così difficile in Android? Questo è impazzito.
funzionato il

Risposte:


389

Ogni istanza di Visualizza chiamate getViewTreeObserver(). Ora, quando si tiene un caso di ViewTreeObserver, è possibile aggiungere un OnScrollChangedListener()ad esso utilizzando il metodo addOnScrollChangedListener().

Puoi vedere ulteriori informazioni su questa classe qui .

Ti consente di essere a conoscenza di ogni evento a scorrimento, ma senza le coordinate. Puoi ottenerli utilizzando getScrollY()o getScrollX()dall'interno dell'ascoltatore.

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

6
Questa risposta dovrebbe essere contrassegnata come corretta. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});in onStart () dove si hsvtrova un'opera HorizontalScrollView. Sospetto che lo stesso funzionerà anche con ScrollView.
JohnnyLambada,

3
Esattamente. questa è la risposta migliore non è necessario estendere HorizontalScrollView. Appena testato con ScrollView, funziona alla grande: 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.getScroll}))
Raffaello C,

5
Dovresti prima controllare ViewTreeObserver.isAlive ()
M.Salomaa

6
Il codice di esempio nella risposta introduce una perdita di memoria. Poiché il metodo è adde non set, tutti i listener verranno conservati fino a quando non verranno rimossi esplicitamente. Quindi la classe anonima utilizzata come implementazione di listener nell'esempio perderà (insieme a tutto ciò a cui fa riferimento, ovvero la classe esterna).
Brett Duncavage,

7
Probabilmente una domanda stupida, ma cos'è rootScrollView?
Iqbal,

49

Questo potrebbe essere molto utile. Usa NestedScrollViewinvece di ScrollView. Libreria di supporto 23.1 ha introdotto un OnScrollChangeListenera NestedScrollView. Quindi puoi fare qualcosa del genere.

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

2
Sicuramente più semplice che tenere traccia del fatto che tu abbia aggiunto il listener prima con getViewTreeObserver (). Grazie!
Anthony Mills,

Ma come utilizzare NestedScrollView come vista di scorrimento orizzontale? Impossibile trovare alcuna risorsa
GensaGames

Cosa succede se desidero utilizzare lo scorrimento orizzontale?
Nikita Axyonov,

7
@ dazed'n'confused NestedScrollViewfa parte della libreria di supporto, il suo setOnScrollChangeListenermetodo non ha requisiti minimi di versione.
Tom

4
Non confondere con View's setOnScrollChangeListenerche richiede livello API 23
Anders Ullnæss

18

Ecco un HorizontalScrollView derivato che ho scritto per gestire le notifiche relative allo scorrimento e alla fine dello scorrimento. Gestisce correttamente quando un utente ha smesso di scorrere attivamente e quando rallenta completamente dopo che un utente ha lasciato andare:

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

}

1
Sembra che sia meglio dell'uso del metodo ViewTreeObserver. Solo perché quando hai un'attività in cui hai caricato più frammenti (ad esempio 3 frammenti con schede scorrevoli) con ListViews e ScrollViews e devi registrare eventi per una vista specifica. Considerando che se ViewTreeObserver è registrato, genererà eventi anche se non è attiva.
Torsten Ojaperv,

3
@ZaBlanc - Per favore, puoi chiamarmi come inizializzare questa classe e gli ascoltatori dalla mia attività, che contiene ScrollView? L'ho implementato bene, ma gli ascoltatori non restituiranno ancora alcun valore.
Edmond Tamas,

Questo funziona davvero! e non c'è bisogno di sdk> 23 per questo :)
Matan Dahan,

5

Se si desidera conoscere la posizione di scorrimento di una vista, è possibile utilizzare la seguente funzione di estensione nella classe Vista:

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

5

Puoi usare NestedScrollViewinvece di ScrollView. Tuttavia, quando si utilizza un Lambda Kotlin, non saprà che si desidera NestedScrollView setOnScrollChangeListenerinvece di quello a View (che è il livello API 23). È possibile risolvere questo problema specificando il primo parametro come NestedScrollView.

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}

Questa dovrebbe essere la risposta accettata perché il metodo che utilizza viewTreeObserver provoca perdite di memoria e deve essere rimosso manualmente, altrimenti verranno aggiunti più osservatori di scorrimento.
Ray Li

1
    // --------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---------

prova ad aggiungere una descrizione per supportare il tuo codice che farà capire a tutti la pastella
Aniruddha Das,

Questo codice è molto simile a un'altra risposta , no?
Sergii,

1

è possibile definire una classe ScrollView personalizzata e aggiungere un'interfaccia da chiamare durante lo scorrimento in questo modo:

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.