Detectar Desplácese hacia arriba y desplácese hacia abajo en ListView

Tengo el siguiente requisito:

  • Al principio, los datos de la página n. ° 2 se extraen del servidor y los elementos se completan en un ListView.

Teniendo en cuenta que tanto la página anterior como la siguiente están disponibles en un escenario, se ha agregado el siguiente código:

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

¿Qué condiciones debo poner para detectar desplazamiento hacia arriba y desplazarse hacia abajo en los siguientes métodos:

  1. void onScroll (vista AbsListView, int firstVisibleItem, int visibleItemCount, int totalItemCount)
  2. void onScrollStateChanged (vista de AbsListView, int scrollState)

Después de la acción: se detecta el desplazamiento hacia arriba y el desplazamiento hacia abajo, en consecuencia, se llamará a un servicio con la página anterior no o la página siguiente, para buscar los elementos que se rellenarán en la vista de lista.

Cualquier entrada será útil.

Pasó por los siguientes enlaces pero no devuelve la acción correcta de desplazamiento hacia arriba / desplazamiento hacia abajo:

enlace 1 enlace 2

intente usar el setOnScrollListener e implemente el onScrollStateChanged con 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; } } }); 

Aquí hay una versión modificada de algunas de las soluciones indicadas anteriormente.

Agregue otra clase 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; } } 

Y una interfaz:

 public interface OnDetectScrollListener { void onUpScrolling(); void onDownScrolling(); } 

Y finalmente cómo usar:

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

En su diseño XML:

  

Este es mi primer tema, no me juzgues con dureza. =)

esta es una implementación 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(mLastFirstVisibleItemfirstVisibleItem) { Log.i("SCROLLING UP","TRUE"); } mLastFirstVisibleItem=firstVisibleItem; } }); 

y si necesita más precisión, puede usar esta clase personalizada de ListView:

 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 ejemplo de uso: (no olvide agregarlo como una etiqueta Xml en su layout.xml)

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

Con todo el método publicado, hay problemas para reconocer cuándo el usuario se desplaza hacia arriba desde el primer elemento o hacia abajo desde el último. Aquí hay otro enfoque para detectar el desplazamiento arriba / abajo:

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

Mi solución funciona perfectamente dando el valor exacto para cada dirección de desplazamiento. distanceFromFirstCellToTop contiene la distancia exacta desde la primera celda hasta la parte superior de la Vista principal. Guardo este valor en previousDistanceFromFirstCellToTop y al desplazarme, lo comparo con el nuevo valor. Si es más bajo, me desplacé hacia arriba, de lo contrario, me desplacé hacia abajo.

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

Para los desarrolladores de Xamarin, la solución es la siguiente:

Nota: no te olvides de ejecutar en el hilo de UI

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

He usado esta solución mucho más 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) { } } 

Para detectar también el desplazamiento con elementos más grandes, prefiero un OnTouch Listener:

 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 downScrolledEventsHappened; @Override public boolean onTouch(View v, MotionEvent event) { float diff = 0; if(event.getAction() == event.ACTION_DOWN){ lastY = event.getY(); downScrolledEventsHappened = new LinkedList(); } 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 0) Log.i("INFO", "Scrolled up"); else Log.i("INFO", "Scrolled down"); } } return false; // don't interrupt the event-chain } }); 

Simplemente configure el detector de desplazamiento en su vista de lista.

Si tiene un encabezado o un pie de página, también debe verificar el conteo visible. Si aumenta, significa que estás desplazándote hacia abajo. (Invierte si hay un pie de página en lugar de encabezado)

Si no tiene ningún encabezado o pie de página en su vista de lista, puede eliminar las líneas que muestran el recuento de elementos 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) { } }); 

y tienen estas variables

 int mLastFirstVisibleItem; int mLastVisibleItemCount; 

Almacene el firstVisibleItem y en la siguiente comprobación deScroll si el nuevo firstVisibleItem es menor o mayor que el anterior.

Pseudocódigo de ejemplo (no probado):

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

Esos métodos no se pueden usar para detectar direcciones de desplazamiento directamente. Hay muchas formas de obtener la dirección. Un código simple (no probado) para uno de estos métodos se explica a continuación:


 la clase pública ScrollTrackingListView extiende ListView {

     private boolean readyForMeasurement = false;
     private Boolean isScrollable = null;
     float privado prevDistanceToEnd = -1.0;
     private ScrollDirectionListener listener = null;

     public ScrollTrackingListView (Contexto contextual) {
         super (contexto);
         en eso();
     }

     public ScrollTrackingListView (Context context, AttributeSet attrs) {
         super (context, attrs);
         en eso();
     }

     public ScrollTrackingListView (Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
         en eso();
     }

     private void init () {
         ViewTreeObserver observer = getViewTreeObserver ();
         observer.addOnGlobalLayoutListener (globalLayoutListener);
         setOnScrollListener (scrollListener);
     }

     privado ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener
             = new ViewTreeObserver.OnGlobalLayoutListener () {

         @Anular
         public void onGlobalLayout () {
             readyForMeasurement = true;
             calculateDistanceToEnd ();
         }

     };

     public void registerScrollDirectionListener (ScrollDirectionListener listener) {
         scrollDirectionListener = oyente;
     }

     public void unregisterScrollDirectionListener () {
         scrollDirectionListener = null;
     }

     privado OnScrollListener scrollListener
             = new OnScrollListener () {

         @Anular
         public void onScrollStateChanged (AbsListView absListView, int i) {
             calculateDistanceToEnd ();
         }

         @Anular
         public void onScroll (AbsListView absListView, int i, int i1, int i2) {
             // Hacer nada
         }

     };

     private void calculateDistanceToEnd () {

         if (readyForMeasurement) {

             // Estoy usando la altura del diseño, la barra de desplazamiento horizontal y
             // contenido junto con desplazamiento hacia abajo desplazamiento

             // computeVerticalScrollExtent se usa para calcular la longitud del pulgar dentro de la pista de la barra de desplazamiento.
             // La longitud del pulgar es una función de la altura de la vista y la longitud del contenido.
             int verticalScrollExtent = computeVerticalScrollExtent ();
             int verticalScrollOffset = computeVerticalScrollOffset ();
             int verticalScrollRange = computeVerticalScrollRange ();
             int horizontalScrollBarHeight = getHorizontalScrollbarHeight ();

             / **
              * 1. Deje que "R" represente el rango de la barra de desplazamiento vertical.  Esto corresponde a la duración del contenido
              * en la vista.
              * 2. Deje que "E" represente la extensión de la barra de desplazamiento vertical.  La extensión es un valor constante y es
              * (probablemente) igual a un valor proporcional a la altura de la vista.
              * 3. Desplazamiento "o" representa la posición actual en el rango que es visible para el usuario.  Puede tomar
              * valores de "0 a E".
              *
              * Ahora DistanceTo End se calcula utilizando estos tres valores de la siguiente manera:
              *
              * DistanceToEnd = (R - o) / E
              *
              * DistanceToEnd mantendrá el valor en unidades NumberOfScreenToEnd.
              *
              * /

             distancia flotanteToEnd =
                     ((float) (verticalScrollRange - verticalScrollOffset)) / ((float) (verticalScrollExtent));

             if (prevDistanceToEnd == -1) {
                  prevDistanceToEnd = distanceToEnd;
             } else {
                  if (listener! = null) {
                      if (distanceToEnd> prevDistanceToEnd) {
                         // El usuario se está desplazando hacia arriba
                          listener.onScrollingUp ();
                      } else {
                         // El usuario se está desplazando hacia arriba
                          listener.onScrollingDown ();
                     }
                  }
                  prevDistanceToEnd = distanceToEnd;
             }

             if (isScrollable == null) {
                 // Compruebe si la altura de la vista es menor que una pantalla (es decir, no se habilita el desplazamiento)
                 if ((horizontalScrollBarHeight + verticalScrollExtent)> = verticalScrollRange) {
                     isScrollable = Boolean.FALSE;
                 } else {
                     isScrollable = Boolean.TRUE;
                 }
             }

         }

     }

     interfaz pública ScrollDirectionListener {

         public void onScrollingUp ();

         public void onScrollingDown ();

     }

 }

La idea es calcular la distancia To End. Si distanceToEnd aumenta, el usuario se desplaza hacia arriba y si disminuye, el usuario se desplaza hacia abajo. Eso también le dará la distancia exacta hasta el final de la lista.

Si solo está tratando de saber si el usuario se desplaza hacia arriba o hacia abajo, puede anular el onInterceptTouchEvent para detectar la dirección de desplazamiento como se muestra a continuación:

     @Anular
     public boolean onInterceptTouchEvent (evento MotionEvent) {
         switch (event.getAction ()) {
             caso MotionEvent.ACTION_DOWN:
                 mInitialX = event.getX ();
                 mInitialY = event.getY ();
                 devolver verdadero;
             caso MotionEvent.ACTION_MOVE:
                 flotación final x = event.getX ();
                 flotación final y = event.getY ();
                 flotación final yDiff = y - mInitialY;  // yDiff menor que 0.0 implica desplazarse hacia abajo mientras yDiff mayor que 0.0 implica desplazarse hacia arriba.  Si bash agregar menos o más que los símbolos, la vista previa se niega a mostrarlo.
                 if (yDiff menor que 0.0) listener.onScrollingDown ();
                 else if (yDiff mayor que 0.0) listener.onScrollingUp ();
                 descanso;
         }
         return super.onInterceptTouchEvent (evento);
     }

Truco sobre detectar desplazamiento hacia arriba o hacia abajo en la vista de lista, simplemente llama a esta función en la función Desplazar en 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(); } } } 

Manera simple de detectar desplazamiento arriba / abajo en la lista de Android

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

no olvides

 yourListView.setOnScrollListener(yourScrollListener); 

Por alguna razón, el documento de Android no cubre esto, y el método utilizado ni siquiera está en los documentos … me tomó un tiempo encontrarlo.

Para detectar si su pergamino está en la parte superior, debería usar esto.

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

Esto verificará si tu desplazamiento está en la parte superior. Ahora, para hacerlo en la parte inferior, tendrías que pasarle la cantidad de hijos que tienes, y verificar con ese número. Puede que tenga que averiguar cuántos están en la pantalla al mismo tiempo y restarlo de su número de hijos. Nunca tuve que hacer eso. Espero que esto ayude

Esto es lo que probaría primero:

1) Cree una interfaz (llamémoslo OnScrollTopOrBottomListener) con estos métodos:

void onScrollTop ();

void onScrollBottom ();

2) En el adaptador de su lista, agregue una instancia de miembro, escriba como la interfaz que creó y proporcione un setter y getter.

3) En la implementación getView () de su adaptador, verifique si el parámetro de posición es 0 o getCount () – 1. También verifique que su instancia OnScrollTopOrBottomListener no sea nula.

4) Si la posición es 0, llame aScrollTopOrBottomListener.onScrollTop (). Si la posición es getCount () – 1, llame aScrollTopOrBottomListener.onScrollBottom ().

5) En su implementación OnScrollTopOrBottomListener, llame a los métodos apropiados para obtener los datos deseados.

Espero que eso ayude de algún modo.

-Brandon

He encontrado problemas al usar algún ejemplo donde el tamaño de la celda de ListView es excelente. Así que he encontrado una solución a mi problema que detecta el más leve movimiento de tu dedo. He simplificado al mínimo posible y es el siguiente:

 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: Uso el MARGEN para no detectar el desplazamiento hasta que encuentre ese margen. Esto evita problemas cuando muestro u oculto vistas y evito parpadear.