¿Puedo tener onScrollListener para un ScrollView?

140

Estoy usando un HorizontalScrollView diseño y necesito identificar que el usuario ha alcanzado el punto inicial y final del desplazamiento.

Porque ListViewhe intentado con el onScrollListenery es posible encontrar el punto inicial y final del desplazamiento.

Traté de hacer lo mismo en mi Scrollviewpero parece que no es posible. ¿Hay alguna otra forma posible de lograr lo que necesito?

arnp
fuente
3
Es posible. Ver la respuesta del usuario 2695685. En resumen, el siguiente en onStarthacer el truco: hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});en onStart () donde hsves una HorizontalScrollViewobra.
JohnnyLambada
acepte cualquier respuesta útil ... si no, publique su propia respuesta ...
Ranjith Kumar
12
¿Por qué es tan difícil detectar un evento de desplazamiento con ScrollView en Android? Esto es una locura imo.
trabajó el

Respuestas:

389

Cada instancia de Ver llamadas getViewTreeObserver(). Ahora, al mantener una instancia de ViewTreeObserver, puede agregarle una OnScrollChangedListener()mediante el métodoaddOnScrollChangedListener() .

Puede ver más información sobre esta clase aquí .

Le permite estar al tanto de cada evento de desplazamiento, pero sin las coordenadas. Sin embargo, puede obtenerlos utilizando getScrollY()o getScrollX()desde el oyente.

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
fuente
66
Esta respuesta debe marcarse como correcta. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});en onStart () donde hsves un HorizontalScrollViewtrabajo. Sospecho que lo mismo funcionará para un ScrollView también.
JohnnyLambada
3
exactamente. Esta es la mejor respuesta. no es necesario extender HorizontalScrollView. Recién probado con un ScrollView, funciona muy 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
55
Primero debe verificar ViewTreeObserver.isAlive ()
M.Salomaa
66
El código de muestra en la respuesta introduce una pérdida de memoria. Como el método es addy no set, todos los oyentes serán retenidos hasta que se eliminen explícitamente. Por lo tanto, la clase anónima utilizada como implementación de escucha en la muestra se filtrará (junto con todo lo que hace referencia, es decir, la clase externa).
Brett Duncavage
77
Probablemente sea una pregunta tonta, pero ¿qué es rootScrollView?
Iqbal
49

Esto puede ser muy útil. Usar en NestedScrollViewlugar de ScrollView. Support Library 23.1 introdujo un OnScrollChangeListenerto NestedScrollView. Entonces puedes hacer algo como esto.

 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
fuente
2
Definitivamente más fácil que hacer un seguimiento de si ha agregado el oyente antes con getViewTreeObserver (). ¡Gracias!
Anthony Mills
Pero, ¿cómo usar NestedScrollView como vista de desplazamiento horizontal? No puedo encontrar ningún recurso
GensaGames
¿Qué pasa si quiero usar el desplazamiento horizontal?
Nikita Axyonov
77
@ dazed'n'confused NestedScrollViewes parte de la biblioteca de soporte, su setOnScrollChangeListenermétodo no tiene un requisito mínimo de versión.
Tom
44
No debe confundirse con View's setOnScrollChangeListenerque requiere API nivel 23
Anders Ullnæss
18

Aquí hay un HorizontalScrollView derivado que escribí para manejar notificaciones sobre desplazamiento y finalización de desplazamiento. Maneja adecuadamente cuando un usuario ha dejado de desplazarse activamente y cuando desacelera por completo después de que un usuario suelta:

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
fuente
1
Parece que esto es mejor que usar el método ViewTreeObserver. Solo porque cuando tiene una actividad en la que tiene múltiples fragmentos cargados (por ejemplo, 3 fragmentos con pestañas deslizantes) con ListViews y ScrollViews y necesita registrar eventos para una vista específica. Mientras que si el ViewTreeObserver está registrado, disparará eventos incluso si no es una vista activa.
Torsten Ojaperv
3
@ZaBlanc - ¿Puede decirme cómo inicializar esta clase y los oyentes de mi Actividad, que contiene ScrollView? Lo he implementado bien, pero los oyentes aún no devolverán ningún valor.
Edmond Tamas
¡Esto realmente funciona! y no hay necesidad de SDK> 23 para ello :)
Matan Dahan
5

Si desea conocer la posición de desplazamiento de una vista, puede usar la siguiente función de extensión en la clase 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
        }
    }
}
mcspayne
fuente
5

Puedes usar en NestedScrollViewlugar de ScrollView. Sin embargo, cuando se utiliza un Kotlin Lambda, no sabrá que desea NestedScrollView's en setOnScrollChangeListenerlugar de uno en View (que es el nivel 23 de API). Puede solucionar esto especificando el primer parámetro como NestedScrollView.

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}
Cristan
fuente
Esta debería ser la respuesta aceptada porque el método que usa viewTreeObserver causa pérdidas de memoria y debe eliminarse manualmente o se agregarán varios observadores de desplazamiento.
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---------
Wirote W.
fuente
intente agregar alguna descripción para apoyar su código que hará que todos entiendan la masa
Aniruddha Das
Este código es muy similar a otra respuesta , ¿no?
Sergii
1

puede definir una clase personalizada de ScrollView y agregar una interfaz que se llamará al desplazarse así:

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
fuente