Android: detecta cuando ScrollView deja de desplazarse

84

Estoy usando un ScrollViewen Android y donde la parte visible del ScrollViewes del mismo tamaño que una de las celdas dentro del Scrollview. Cada "celda" tiene la misma altura. Entonces, lo que estoy tratando de hacer es encajar en su posición después de que ScrollViewse haya desplazado.

Actualmente, estoy detectando cuándo el usuario ha tocado el ScrollViewy cuándo ha comenzado a desplazarse y a trabajar desde allí, pero tiene bastantes errores. También debe funcionar cuando el usuario simplemente lo mueve, se desplaza y luego desacelera.

En iPhone hay una función que es algo así como didDeceleratey allí puedo hacer cualquier código que quiera cuando ScrollViewhaya terminado de desplazarse. ¿Existe tal cosa con Android? ¿O hay algún código que podría mirar para encontrar una mejor manera de hacerlo?

Revisé los documentos de Android y no pude encontrar nada de eso.

usuario1053691
fuente

Respuestas:

101

Recientemente tuve que implementar la función que describiste. Lo que hice fue tener un Runnable comprobando si ScrollView había dejado de desplazarse comparando el valor devuelto por getScrollY () cuando onTouchEvent se activa por primera vez con el valor devuelto después de un tiempo definido por la variable newCheck .

Vea el código a continuación (solución de trabajo):

public class MyScrollView extends ScrollView{

private Runnable scrollerTask;
private int initialPosition;

private int newCheck = 100;
private static final String TAG = "MyScrollView";

public interface OnScrollStoppedListener{
    void onScrollStopped();
}

private OnScrollStoppedListener onScrollStoppedListener;

public MyScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);

    scrollerTask = new Runnable() {

        public void run() {

            int newPosition = getScrollY();
            if(initialPosition - newPosition == 0){//has stopped

                if(onScrollStoppedListener!=null){

                    onScrollStoppedListener.onScrollStopped();
                }
            }else{
                initialPosition = getScrollY();
                MyScrollView.this.postDelayed(scrollerTask, newCheck);
            }
        }
    };
}

public void setOnScrollStoppedListener(MyScrollView.OnScrollStoppedListener listener){
    onScrollStoppedListener = listener;
}

public void startScrollerTask(){

    initialPosition = getScrollY();
    MyScrollView.this.postDelayed(scrollerTask, newCheck);
}

}

Luego tengo:

scroll.setOnTouchListener(new OnTouchListener() {

        public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_UP) {

                scroll.startScrollerTask();
            }

            return false;
        }
});
scroll.setOnScrollStoppedListener(new OnScrollStoppedListener() {

        public void onScrollStopped() {

            Log.i(TAG, "stopped");

        }
});

Por cierto, utilicé algunas ideas de otras respuestas para hacer esto en mi aplicación. Espero que esto ayude. Cualquier pregunta no dude en preguntar. Salud.

tulio84z
fuente
11
Gracias, he estado buscando durante años algo como esto. Se usa para HorizontalScrollView en su lugar, que (obviamente) también funciona. Solo getScrollY () debe ser reemplazado por getScrollX ()
Schnodahipfe
1
Los probé todos aquí. Este es el ÚNICO que funciona.
Mike6679
23

Aquí hay otra solución para, en mi humilde opinión, el error del evento OnEndScroll faltante en ScrollView.

Está inspirado en una respuesta ambigua . Simplemente coloque esta clase en su proyecto (cambie el paquete para que coincida con el suyo) y use el siguiente xml

package com.thecrag.components.ui;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

public class ResponsiveScrollView extends ScrollView {

    public interface OnEndScrollListener {
        public void onEndScroll();
    }

    private boolean mIsFling;
    private OnEndScrollListener mOnEndScrollListener;

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

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

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

    @Override
    public void fling(int velocityY) {
        super.fling(velocityY);
        mIsFling = true;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);
        if (mIsFling) {
            if (Math.abs(y - oldY) < 2 || y >= getMeasuredHeight() || y == 0) {
                if (mOnEndScrollListener != null) {
                    mOnEndScrollListener.onEndScroll();
                }
                mIsFling = false;
            }
        }
    }

    public OnEndScrollListener getOnEndScrollListener() {
        return mOnEndScrollListener;
    }

    public void setOnEndScrollListener(OnEndScrollListener mOnEndScrollListener) {
        this.mOnEndScrollListener = mOnEndScrollListener;
    }

}

nuevamente cambiando el nombre del paquete para que coincida con su proyecto

<com.thecrag.components.ui.ResponsiveScrollView
        android:id="@+id/welcome_scroller"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/welcome_scroll_command_help_container"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/welcome_header_text_thecrag"
        android:layout_margin="6dp">
    ....
</com.thecrag.components.ui.ResponsiveScrollView>
concepto
fuente
2
+1 Bonita y sencilla respuesta. Cómo cambio el valor de la diferencia 2para 0.5freflejar el original ScrollViews' MAX_SCROLL_FACTORvalor.
Phil
2
Esta implementación es buena pero falló cuando la vista de desplazamiento no se llenó al principio. Aquí hay una solución (para una vista de desplazamiento horizontal, así que reemplace todo x por y para obtener uno vertical): @Override protected void onScrollChanged (int x, int y, int oldX, int oldY) {super.onScrollChanged (x, y , oldX, oldY); if (Math.abs (x - oldX) <SCROLL_FINISHED_THRESHOLD && x! = lastStopX || x> = getMeasuredWidth () || x == 0) {lastStopX = x; if (mOnEndScrollListener! = null) {mOnEndScrollListener.onScrollEnded (); }}}
Snicolas
Esto no siempre funciona. Como en el emulador, no siempre se llama public void fling (int velocityY). El umbral del final del desplazamiento a veces es de más de 20 px.
Andrew Feng
9

Subclasé (Horizontal) ScrollView e hice algo como esto:

@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
    if (Math.abs(x - oldX) > SlowDownThreshold) {  
        currentlyScrolling = true;
    } else {
        currentlyScrolling = false;
        if (!currentlyTouching) {
            //scrolling stopped...handle here
        }
    }
    super.onScrollChanged(x, y, oldX, oldY);
}

Usé un valor de 1 para SlowDownThreshold ya que siempre parece ser la diferencia del último evento onScrollChanged.

Para que esto se comporte correctamente al arrastrar lentamente, tuve que hacer esto:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            currentlyTouching = true;
    }
    return super.onInterceptTouchEvent(event);
}

@Override
public boolean onTouch(View view, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            currentlyTouching = false;
            if (!currentlyScrolling) {
                //I handle the release from a drag here
                return true;
            }
    }
    return false;
}
Ross Hambrick
fuente
Buena respuesta. La única adición. En onInterceptTouchEvent () también ha manejado MotionEvent.ACTION_UP y MotionEvent.ACTION_CANCEL; de lo contrario, la función Toque actual permanecerá verdadera en caso de que abra otra actividad con el clic.
Cynichniy Bandera
Una solución más: en onScrollChanged () también debe verificar el final del desplazamiento incluso si la desaceleración aún no es 1. Así: boolean end = (oldx> x)? (x <= 0): (x> = getMaxScrollAmount ()); if (Math.abs (x - oldx)> 1 &&! end) {inicio de desplazamiento; } else {scroll end}
Cynichniy Bandera
Esto no funciona en algunos dispositivos, es decir, GS3. En mi GS3, la diferencia entre y y y antiguo PUEDE ser 1 durante la mitad del desplazamiento. Sin embargo, funciona perfectamente en mi Nexus 4. No estoy seguro de por qué.
Bryan
Tampoco funciona en Samsung Galaxy Note 2 y Asus Memo Pad 7. También veo incrementos de 1 en el medio del desplazamiento en estos dispositivos.
Oliver Hausler
7

Mi enfoque es determinar el estado de desplazamiento mediante una marca de tiempo que cambia cada vez que se llama a onScrollChanged (). Es muy fácil determinar cuándo comienza y cuándo termina el desplazamiento. También puede cambiar el umbral (yo uso 100 ms) para corregir la sensibilidad.

public class CustomScrollView extends ScrollView {
    private long lastScrollUpdate = -1;

    private class ScrollStateHandler implements Runnable {

        @Override
        public void run() {
            long currentTime = System.currentTimeMillis();
            if ((currentTime - lastScrollUpdate) > 100) {
                lastScrollUpdate = -1;
                onScrollEnd();
            } else {
                postDelayed(this, 100);
            }
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (lastScrollUpdate == -1) {
            onScrollStart();
            postDelayed(new ScrollStateHandler(), 100);
        }

        lastScrollUpdate = System.currentTimeMillis();
    }

    private void onScrollStart() {
        // do something
    }

    private void onScrollEnd() {
        // do something
    }

}
Lin Yu Cheng
fuente
Gran solucion Funciona como se supone.
Oliver Hausler
Gracias por tu codigo. Cómo detectar el método de escucha de inicio de desplazamiento desde su código. por favor mire mi pregunta stackoverflow.com/questions/27864700/…
MAMurali
6

Aquí hay otra solución, bastante simple y limpia en mi opinión, naturalmente inspirada en las respuestas anteriores. Básicamente, una vez que el usuario finalizó el gesto, verifique si getScrollY () todavía está cambiando, después de un breve retraso (aquí 50 ms).

public class ScrollViewWithOnStopListener extends ScrollView {

    OnScrollStopListener listener;

    public interface OnScrollStopListener {
        void onScrollStopped(int y);
    }

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

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                checkIfScrollStopped();
        }

        return super.onTouchEvent(ev);
    }

    int initialY = 0;

    private void checkIfScrollStopped() {
        initialY = getScrollY();
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                int updatedY = getScrollY();
                if (updatedY == initialY) {
                    //we've stopped
                    if (listener != null) {
                        listener.onScrollStopped(getScrollY());
                    }
                } else {
                    initialY = updatedY;
                    checkIfScrollStopped();
                }
            }
        }, 50);
    }

    public void setOnScrollStoppedListener(OnScrollStopListener yListener) {
        listener = yListener;
    }
}
Aleksandarf
fuente
5

Mi enfoque para esta pregunta es usar un temporizador para verificar los siguientes 2 "eventos".

1) onScrollChanged () dejó de ser llamado

2) El dedo del usuario se levanta de la vista de desplazamiento

public class CustomScrollView extends HorizontalScrollView {

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

Timer ntimer = new Timer();
MotionEvent event;

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) 
{
    checkAgain();
    super.onScrollChanged(l, t, oldl, oldt);
}

public void checkAgain(){
    try{
        ntimer.cancel();
        ntimer.purge();
    }
    catch(Exception e){}
    ntimer = new Timer();
    ntimer.schedule(new TimerTask() {

        @Override
        public void run() {

            if(event.getAction() == MotionEvent.ACTION_UP){
                // ScrollView Stopped Scrolling and Finger is not on the ScrollView
            }
            else{
                // ScrollView Stopped Scrolling But Finger is still on the ScrollView
                checkAgain();
            }
        }
    },100);

}

@Override
public boolean onTouchEvent(MotionEvent event) {
    this.event = event; 
    return super.onTouchEvent(event);
    }
}
ZeroG
fuente
3

Mi solución es una variación de la gran solución de Lin Yu Cheng y también detecta cuándo se inició y se detuvo el desplazamiento.

Paso 1. Defina un HorizontalScrollView y OnScrollChangedListener:

CustomHorizontalScrollView scrollView = (CustomHorizontalScrollView) findViewById(R.id.horizontalScrollView);

horizontalScrollListener = new CustomHorizontalScrollView.OnScrollChangedListener() {
    @Override
    public void onScrollStart() {
        // Scrolling has started. Insert your code here...
    }

    @Override
    public void onScrollEnd() {
         // Scrolling has stopped. Insert your code here...
    }
};

scrollView.setOnScrollChangedListener(horizontalScrollListener);

Paso 2. Agregue la clase CustomHorizontalScrollView:

public class CustomHorizontalScrollView extends HorizontalScrollView {
    public interface OnScrollChangedListener {
        // Developer must implement these methods.
        void onScrollStart();
        void onScrollEnd();
    }

    private long lastScrollUpdate = -1;
    private int scrollTaskInterval = 100;
    private Runnable mScrollingRunnable;
    public OnScrollChangedListener mOnScrollListener;

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

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

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

    private void init(Context context) {
        // Check for scrolling every scrollTaskInterval milliseconds
        mScrollingRunnable = new Runnable() {
            public void run() {
                if ((System.currentTimeMillis() - lastScrollUpdate) > scrollTaskInterval) {
                    // Scrolling has stopped.
                    lastScrollUpdate = -1;
                    //CustomHorizontalScrollView.this.onScrollEnd();
                    mOnScrollListener.onScrollEnd();
                } else {
                    // Still scrolling - Check again in scrollTaskInterval milliseconds...
                    postDelayed(this, scrollTaskInterval);
                }
            }
        };
    }

    public void setOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener) {
        this.mOnScrollListener = onScrollChangedListener;
    }

    public void setScrollTaskInterval(int scrollTaskInterval) {
        this.scrollTaskInterval = scrollTaskInterval;
    }

    //void onScrollStart() {
    //    System.out.println("Scroll started...");
    //}

    //void onScrollEnd() {
    //    System.out.println("Scroll ended...");
    //}

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (mOnScrollListener != null) {
            if (lastScrollUpdate == -1) {
                //CustomHorizontalScrollView.this.onScrollStart();
                mOnScrollListener.onScrollStart();
                postDelayed(mScrollingRunnable, scrollTaskInterval);
            }

            lastScrollUpdate = System.currentTimeMillis();
        }
    }
}
D. Clarke
fuente
2

Intente echar un vistazo a esta pregunta aquí en StackOverflow; no es exactamente lo mismo que su pregunta, pero le da una idea de cómo puede administrar el evento de desplazamiento de un archivo ScrollView.

Básicamente, necesita crear el suyo propio CustomScrollViewextendiendo ScrollViewy anulando onScrollChanged(int x, int y, int oldx, int oldy). Luego, debe hacer referencia a esto en su archivo de diseño en lugar del estándar ScrollViewcomo com.mypackage.CustomScrollView.

Kaspermoerch
fuente
Este onScrollChanged () es en realidad lo que estoy usando en este momento. Pero gracias.
user1053691
2

Para un caso simple como el que describió, probablemente pueda salirse con la suya reemplazando el método de lanzamiento en su vista de desplazamiento personalizada. Se llama al método Fling para realizar una "desaceleración" cada vez que el usuario levanta el dedo de la pantalla.

Entonces, lo que debes hacer es algo como esto:

  1. Subclase ScrollView.

    public class MyScrollView extends ScrollView { 
    
        private Scroller scroller;  
        private Runnable scrollerTask; 
    
        //...
    
        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            scroller = new Scroller(getContext()); //or OverScroller for 3.0+
            scrollerTask = new Runnable() {
                @Override
                public void run() {
                    scroller.computeScrollOffset();
                    scrollTo(0, scroller.getCurrY());
    
                    if (!scroller.isFinished()) {
                        MyScrollView.this.post(this);
                    } else {
                        //deceleration ends here, do your code
                    }
                }
            };
            //...
        }
    }
    
  2. Método de lanzamiento de subclase y NO llame a la implementación de superclase.

    @Override  
    public void fling(int velocityY) {  
        scroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0, container.getHeight());
        post(scrollerTask);  
    
        //add any extra functions you need from android source code:
        //show scroll bars
        //change focus
        //etc.
    }
    
  3. Fling no se activará si el usuario deja de desplazarse antes de levantar el dedo (velocityY == 0). En caso de que desee interceptar este tipo de eventos también, anule onTouchEvent.

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean eventConsumed = super.onTouchEvent(ev);
        if (eventConsumed && ev.getAction() == MotionEvent.ACTION_UP) {
            if (scroller.isFinished()) {
                //do your code
            }
        }
        return eventConsumed;
    }
    

NOTA Aunque esto funciona, anular el método de lanzamiento puede ser una mala idea. Es público, pero apenas está diseñado para subclases. En este momento hace 3 cosas: inicia el lanzamiento para mScroller privado, maneja posibles cambios de enfoque y muestra barras de desplazamiento. Esto podría cambiar en futuras versiones de Android. Por ejemplo, la instancia privada de mScroller cambió su clase de Scroller a OvershootScroller entre 2.3 y 3.0. Tienes que tener en cuenta todas estas pequeñas diferencias. En cualquier caso, prepárate para consecuencias imprevistas en el futuro.

Alexey
fuente
¿Qué es container.getHeight () en el segundo paso (2.)?
Mitul Varmora
1
contenedor es la vista que está dentro de scrollView. Dado que scrollView puede contener solo 1 hijo, puede reemplazar el contenedor con getChildAt (0).
Alexey
2

Aquí hay algunas respuestas excelentes, pero mi código puede detectar cuándo se detiene el desplazamiento sin tener que extender la clase ScrollView. cada instancia de vista puede llamar a getViewTreeObserver (). al mantener esta instancia de ViewTreeObserver, puede agregar un OnScrollChangedListener usando la función addOnScrollChangedListener ().

declare lo siguiente:

private ScrollView scrollListener;
private volatile long milesec;

private Handler scrollStopDetector;
private Thread scrollcalled = new Thread() {

    @Override
    public void run() { 
        if (System.currentTimeMillis() - milesec > 200) {
            //scroll stopped - put your code here
        }
    }
}; 

y en tu onCreate (u otro lugar) agrega:

    scrollListener = (ScrollView) findViewById(R.id.scroll);

    scrollListener.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {

        @Override
        public void onScrollChanged() {
            milesec = System.currentTimeMillis();
            scrollStopDetector.postDelayed(scrollcalled, 200);
        }
    });

es posible que desee tomar más o menos tiempo entre estas comprobaciones, pero cuando se desplaza, este listner se llama muy rápido, por lo que funcionará muy rápido.

Zbun
fuente
No lo he intentado en este caso, pero por mi experiencia, supongo que la llamada repetitiva y recíproca de postDelayed de onScrollChanged provocará pérdidas de memoria.
Oliver Hausler
2

Aquí está mi solución que incluye seguimiento de desplazamiento y finalización de desplazamiento:

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
2

Creo que esto ha surgido en el pasado. AFAIK, no puedes detectar eso fácilmente . Mi sugerencia es que eches un vistazo ScrollView.java(así es como hacemos las cosas en la tierra de Android :)) y descubras cómo puedes extender la clase para proporcionar la funcionalidad que estás buscando. Esto es lo que probaría primero:

 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
     if (mScroller.isFinished()) {
         // do something, for example call a listener
     }
 }
Felix
fuente
¡Bien, no noté el método isFinished!
Jordi Coscolla
3
Gracias. ¿Cómo accedo a este método isFinished ()? Eché un vistazo al archivo ScrollView.java y mScroller es privado y no puedo decir que no haya forma de acceder a él.
user1053691
Hmm, de hecho. No miré lo suficientemente cerca. Por eso dije que lo intentaría primero :). Se podría hacer otras cosas, como llama a la pista onScrollChanged, scrollToo cualquier otro método de desplazamiento relacionada. Alternativamente, puede copiar todo el ScrollViewcódigo y crear ese campo protected. Sin embargo, no lo recomendaría.
Felix
1
Se accedió a mScroller a través de la reflexión. No funciona también, isFinished () se activa cada vez que la vista de desplazamiento alcanza la posición de su dedo, que es casi continuamente. El evento de parada de desplazamiento debería dispararse cuando el usuario suelta el dedo y el desplazador deja de desplazarse.
amik
20
Esta respuesta parecía realmente prometedora, pero como quedó sin salida, ¿tal vez debería eliminarse?
spaaarky21
2

este es un hilo antiguo, pero me gustaría agregar una solución más corta que se me ocurrió:

 buttonsScrollView.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->

            handler.removeCallbacksAndMessages(null)

            handler.postDelayed({
                  //YOUR CODE TO BE EXECUTED HERE
                },1000)
        }

Naturalmente, hay un retraso de 1000 milisegundos. Ajústelo si es necesario.

pepinobr
fuente
1

Hice algunas mejoras en la respuesta de ZeroG. Principalmente la cancelación del exceso de llamadas de tareas e implementación de todo como un OnTouchListener privado, por lo que todo el código de detección de desplazamiento estaría en un solo lugar.

Pegue el siguiente código en su propia implementación de ScrollView:

private class ScrollFinishHandler implements OnTouchListener
{
        private static final int SCROLL_TASK_INTERVAL = 100;

        private Runnable    mScrollerTask;
        private int         mInitialPosition = 0;

        public ScrollFinishHandler()
        {
            mScrollerTask = new Runnable() {

                public void run() {

                    int newPosition = getScrollY();
                    if(mInitialPosition - newPosition == 0)
                    {//has stopped

                       onScrollStopped(); // Implement this on your main ScrollView class
                    }else{
                        mInitialPosition = getScrollY();
                        ExpandingLinearLayout.this.postDelayed(mScrollerTask, SCROLL_TASK_INTERVAL);
                    }
                }
            };
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) 
        {
            if (event.getAction() == MotionEvent.ACTION_UP) 
            {

                startScrollerTask();
            }
            else
            {
                stopScrollerTask();
            }

            return false;
        }

}

Y luego en su implementación de ScrollView:

setOnTouchListener( new ScrollFinishHandler() );
Vaiden
fuente
-1
        this.getListView().setOnScrollListener(new OnScrollListener(){
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {}

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
                     if( firstVisibleItem + visibleItemCount >= totalItemCount ) 
                              // Last item is shown...

            }

Espero que el fragmento ayude :)

Jordi Coscolla
fuente
Desafortunadamente, estoy usando un ScrollView, no un ListView. Aunque gracias.
user1053691
1
¿Hola? ScrollView no ListView.
Kevin Parker