dentro android android-listview scrollbar

listview dentro de scrollview android



Detectar Desplácese hacia arriba y desplácese hacia abajo en ListView (16)

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


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


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:

<com.example.view.ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent"/>

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


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


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:

public class ScrollTrackingListView extends ListView { private boolean readyForMeasurement = false; private Boolean isScrollable = null; private float prevDistanceToEnd = -1.0; private ScrollDirectionListener listener = null; public ScrollTrackingListView(Context context) { super(context); init(); } public ScrollTrackingListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollTrackingListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { ViewTreeObserver observer = getViewTreeObserver(); observer.addOnGlobalLayoutListener(globalLayoutListener); setOnScrollListener(scrollListener); } private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { readyForMeasurement = true; calculateDistanceToEnd(); } }; public void registerScrollDirectionListener(ScrollDirectionListener listener) { scrollDirectionListener = listener; } public void unregisterScrollDirectionListener() { scrollDirectionListener = null; } private OnScrollListener scrollListener = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView, int i) { calculateDistanceToEnd(); } @Override public void onScroll(AbsListView absListView, int i, int i1, int i2) { // Do nothing } }; private void calculateDistanceToEnd() { if (readyForMeasurement) { // I''m using the height of the layout, horizontal scrollbar and // content along with scroll down offset // computeVerticalScrollExtent is used to compute the length of the thumb within the scrollbar''s track. // The length of the thumb is a function of the view height and the content length. int verticalScrollExtent = computeVerticalScrollExtent(); int verticalScrollOffset = computeVerticalScrollOffset(); int verticalScrollRange = computeVerticalScrollRange(); int horizontalScrollBarHeight = getHorizontalScrollbarHeight(); /** * 1. Let "R" represent the range of the vertical scrollbar. This corresponds to the length of the content * in the view. * 2. Let "E" represent the extent of the vertical scrollbar. The extent is a constant value and is * (probably) equal to a value proportional to the height of the view. * 3. Offset "o" represents the current position in the range that is visible to the user. It can take * values from "0 to E". * * Now the DistanceToEnd is calculated using these three values as follows : * * DistanceToEnd = (R - o) / E * * DistanceToEnd will hold the value in NumberOfScreenToEnd units. * */ float distanceToEnd = ((float)(verticalScrollRange - verticalScrollOffset))/((float)(verticalScrollExtent)); if(prevDistanceToEnd == -1) { prevDistanceToEnd = distanceToEnd; } else { if(listener != null) { if(distanceToEnd > prevDistanceToEnd) { // User is scrolling up listener.onScrollingUp(); } else { // User is scrolling up listener.onScrollingDown(); } } prevDistanceToEnd = distanceToEnd; } if(isScrollable == null) { // Check if the view height is less than a screen (i.e., no scrolling is enabled) if((horizontalScrollBarHeight + verticalScrollExtent) >= verticalScrollRange) { isScrollable = Boolean.FALSE; } else { isScrollable = Boolean.TRUE; } } } } public interface 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:

@Override public boolean onInterceptTouchEvent(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; // yDiff less than 0.0 implies scrolling down while yDiff greater than 0.0 implies scrolling up. If I try to add the less than or greater than symbols, the preview refuses to display it. if(yDiff less than 0.0) listener.onScrollingDown(); else if(yDiff greater than 0.0) listener.onScrollingUp(); break; } return super.onInterceptTouchEvent(event); }


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.


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


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


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


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


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


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;


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


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(mLastFirstVisibleItem<firstVisibleItem) { Log.i("SCROLLING DOWN","TRUE"); } if(mLastFirstVisibleItem>firstVisibleItem) { 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 } });


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


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