2011-12-12 13 views
12

Tengo un listView. Cuando me desplazo y me detengo en un lugar en particular.Lista de AndroidVer encontrar la cantidad de píxeles desplazados

¿Cómo puedo obtener la cantidad de píxeles que desplacé (desde arriba)?

He intentado usar conseguir listView.getScrollY(), pero devuelve 0.

+0

Aquí está [cómo] (http://stackoverflow.com/questions/12727594/android-listview-current-scroll-location-y-pixels/35594825#35594825) – Sarasranglt

Respuesta

19

Tuve el mismo problema.

No puedo usar View.getScrollY() porque siempre devuelve 0 y no puedo usar OnScrollListener.onScroll (...) porque funciona con posiciones que no tienen píxeles. No puedo subclasificar ListView y reemplazar onScrollChanged (...) porque sus valores de parámetro siempre son 0. Meh.

Todo lo que quiero saber es la cantidad que los niños (es decir, el contenido de la vista de lista) se desplazaron hacia arriba o hacia abajo. Así que se me ocurrió una solución. Rastreo a uno de los niños (o puede decir una de las "filas") y sigo su cambio de posición vertical.

Aquí está el código:

public class ObservableListView extends ListView { 

public static interface ListViewObserver { 
    public void onScroll(float deltaY); 
} 

private ListViewObserver mObserver; 
private View mTrackedChild; 
private int mTrackedChildPrevPosition; 
private int mTrackedChildPrevTop; 

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

@Override 
protected void onScrollChanged(int l, int t, int oldl, int oldt) { 
    super.onScrollChanged(l, t, oldl, oldt); 
    if (mTrackedChild == null) { 
     if (getChildCount() > 0) { 
      mTrackedChild = getChildInTheMiddle(); 
      mTrackedChildPrevTop = mTrackedChild.getTop(); 
      mTrackedChildPrevPosition = getPositionForView(mTrackedChild); 
     } 
    } else { 
     boolean childIsSafeToTrack = mTrackedChild.getParent() == this && getPositionForView(mTrackedChild) == mTrackedChildPrevPosition; 
     if (childIsSafeToTrack) { 
      int top = mTrackedChild.getTop(); 
      if (mObserver != null) { 
       float deltaY = top - mTrackedChildPrevTop; 
       mObserver.onScroll(deltaY); 
      } 
      mTrackedChildPrevTop = top; 
     } else { 
      mTrackedChild = null; 
     } 
    } 
} 

private View getChildInTheMiddle() { 
    return getChildAt(getChildCount()/2); 
} 

public void setObserver(ListViewObserver observer) { 
    mObserver = observer; 
} 

} 

par de notas:

  • sobreescribimos onScrollChanged (...) ya que ésta se llama cuando se desplaza la vista de lista (sólo sus parámetros son inútiles)
  • luego elegimos un niño (fila) desde el centro (no tiene que ser precisamente el niño en el medio)
  • cada vez que se produce desplazamiento calculamos movimiento vertical basado en la posición anterior (getTop()) del niño rastreado
  • dejamos de rastrear a un niño cuando no es seguro realizar un seguimiento (p. ej.en los casos en que podría quedó reutilizados)
+0

Si agrega una velocidad calculada (por ejemplo, delta_pos/delta_time), tenga en cuenta que la vista deja de llamar aScrollChanged cuando se detiene el desplazamiento, por lo que puede haber un largo tiempo entre la última llamada de un desplazamiento y la primera llamada del siguiente; simplemente ignóralo como (delta_time <100) o algo así. – cdhabecker

+0

he usado tu camino pero inicialmente me da 0 valores ¿puedes decirme por qué sucede esto también estoy usando PullToRefresh así que puedes guiarme –

+0

cuando se invoca al oyente 'onScroll' cuando listview initialize, no es el toque de uso – Ninja

0

tratar de implementar OnScrollListener:

list.setOnScrollListener(new OnScrollListener() { 
      @Override 
      public void onScrollStateChanged(AbsListView view, int scrollState) { 


        int last = view.getLastVisiblePosition(); 
        break; 
       } 
      } 

      @Override 
      public void onScroll(AbsListView view, int firstVisibleItem, 
        int visibleItemCount, int totalItemCount) { 
      } 
     }); 
+0

view.getLastVisiblePosition() .. devuelve la posición. ¿Hay alguna forma de obtener los píxeles? – ShineDown

+0

@Jojo ¿por qué quieres obtener los píxeles en lugar de la posición? –

+2

si un elemento de listView se desplaza la mitad, entonces quiero que se dispare la cantidad. – ShineDown

6

no puede obtener píxeles desde la parte superior de la lista (porque entonces necesita diseñar todas las vistas desde la parte superior de la lista; puede haber muchos elementos). Pero puede obtener píxeles del primer elemento visible: int pixels = listView.getChildAt(0).getTop(); generalmente será cero o número negativo - muestra la diferencia entre la parte superior de la lista Vista y parte superior de la primera vista en la lista

+0

Gracias Jin35, esto me permite encontrar el desplazamiento usando la primera posición visible y la altura del niño. –

5

edición: He mejorado en esta clase para evitar algunos momentos que la pista estaba perdiendo debido a los puntos de vista de ser demasiado grande y no conseguir una adecuada getTop()

Esta nueva solución utiliza 4 puntos de seguimiento:

  • primer hijo, inferiores
  • hijo del medio, los mejores
  • hijo del medio, inferior
  • último hijo, superior

que se asegura de que siempre tiene un isSafeToTrack es igual a true

import android.view.View; 
import android.widget.AbsListView; 

/** 
* Created by budius on 16.05.14. 
* This improves on Zsolt Safrany answer on stack-overflow (see link) 
* by making it a detector that can be attached to any AbsListView. 
* http://stackoverflow.com/questions/8471075/android-listview-find-the-amount-of-pixels-scrolled 
*/ 
public class PixelScrollDetector implements AbsListView.OnScrollListener { 
    private final PixelScrollListener listener; 

    private TrackElement[] trackElements = { 
     new TrackElement(0), // top view, bottom Y 
     new TrackElement(1), // mid view, bottom Y 
     new TrackElement(2), // mid view, top Y 
     new TrackElement(3)};// bottom view, top Y 

    public PixelScrollDetector(PixelScrollListener listener) { 
     this.listener = listener; 
    } 

    @Override 
    public void onScrollStateChanged(AbsListView view, int scrollState) { 
     // init the values every time the list is moving 
     if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || 
     scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { 
     for (TrackElement t : trackElements) 
      t.syncState(view); 
     } 
    } 

    @Override 
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
     boolean wasTracked = false; 
     for (TrackElement t : trackElements) { 
     if (!wasTracked) { 
      if (t.isSafeToTrack(view)) { 
       wasTracked = true; 
       if (listener != null) 
        listener.onScroll(view, t.getDeltaY()); 
       t.syncState(view); 
      } else { 
       t.reset(); 
      } 
     } else { 
      t.syncState(view); 
     } 
     } 
    } 

    public static interface PixelScrollListener { 
     public void onScroll(AbsListView view, float deltaY); 
    } 

    private static class TrackElement { 

     private final int position; 

     private TrackElement(int position) { 
     this.position = position; 
     } 

     void syncState(AbsListView view) { 
     if (view.getChildCount() > 0) { 
      trackedChild = getChild(view); 
      trackedChildPrevTop = getY(); 
      trackedChildPrevPosition = view.getPositionForView(trackedChild); 
     } 
     } 

     void reset() { 
     trackedChild = null; 
     } 

     boolean isSafeToTrack(AbsListView view) { 
     return (trackedChild != null) && 
      (trackedChild.getParent() == view) && (view.getPositionForView(trackedChild) == trackedChildPrevPosition); 
     } 

     int getDeltaY() { 
     return getY() - trackedChildPrevTop; 
     } 

     private View getChild(AbsListView view) { 
     switch (position) { 
      case 0: 
       return view.getChildAt(0); 
      case 1: 
      case 2: 
       return view.getChildAt(view.getChildCount()/2); 
      case 3: 
       return view.getChildAt(view.getChildCount() - 1); 
      default: 
       return null; 
     } 
     } 

     private int getY() { 
     if (position <= 1) { 
      return trackedChild.getBottom(); 
     } else { 
      return trackedChild.getTop(); 
     } 
     } 

     View trackedChild; 
     int trackedChildPrevPosition; 
     int trackedChildPrevTop; 
    } 
} 

respuesta original:

En primer lugar quiero dar las gracias @ zsolt-safrany por su respuesta, eso fue grandioso, total reconocimiento para él.

Pero entonces quiero presentar mi mejora con respecto a su respuesta (todavía es más o menos su respuesta, a sólo unas pocas mejoras)

Mejoras:

  • Es un tipo separado "detector gesto" de clase que se puede agregar a cualquier clase que se extiende AbsListView llamando al .setOnScrollListener(), por lo que es un enfoque más flexible.

  • Está utilizando el cambio en el estado de desplazamiento para preasignar el elemento secundario con seguimiento, por lo que no "pierde" un pase onScroll para asignar su posición.

  • Se recalcula el niño rastreado en cada pase onScroll para evitar que falte un pase aleatorio en el desplazamiento para recalcular el niño. (Esto podría ser más eficiente al almacenar en caché algunas alturas y solo volver a calcular después de cierta cantidad de desplazamiento).

creo que sirve

import android.view.View; 
import android.widget.AbsListView; 

/** 
* Created by budius on 16.05.14. 
* This improves on Zsolt Safrany answer on stack-overflow (see link) 
* by making it a detector that can be attached to any AbsListView. 
* http://stackoverflow.com/questions/8471075/android-listview-find-the-amount-of-pixels-scrolled 
*/ 
public class PixelScrollDetector implements AbsListView.OnScrollListener { 
    private final PixelScrollListener listener; 
    private View mTrackedChild; 
    private int mTrackedChildPrevPosition; 
    private int mTrackedChildPrevTop; 

    public PixelScrollDetector(PixelScrollListener listener) { 
     this.listener = listener; 
    } 

    @Override 
    public void onScrollStateChanged(AbsListView view, int scrollState) { 
     // init the values every time the list is moving 
     if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || 
     scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { 
     if (mTrackedChild == null) { 
      syncState(view); 
     } 
     } 
    } 

    @Override 
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
     if (mTrackedChild == null) { 
     // case we don't have any reference yet, try again here 
     syncState(view); 
     } else { 
     boolean childIsSafeToTrack = (mTrackedChild.getParent() == view) && (view.getPositionForView(mTrackedChild) == mTrackedChildPrevPosition); 
     if (childIsSafeToTrack) { 
      int top = mTrackedChild.getTop(); 
      if (listener != null) { 
       float deltaY = top - mTrackedChildPrevTop; 
       listener.onScroll(view, deltaY); 
      } 
      // re-syncing the state make the tracked child change as the list scrolls, 
      // and that gives a much higher true state for `childIsSafeToTrack` 
      syncState(view); 
     } else { 
      mTrackedChild = null; 
     } 
     } 
    } 

    private void syncState(AbsListView view) { 
     if (view.getChildCount() > 0) { 
     mTrackedChild = getChildInTheMiddle(view); 
     mTrackedChildPrevTop = mTrackedChild.getTop(); 
     mTrackedChildPrevPosition = view.getPositionForView(mTrackedChild); 
     } 
    } 

    private View getChildInTheMiddle(AbsListView view) { 
     return view.getChildAt(view.getChildCount()/2); 
    } 

    public static interface PixelScrollListener { 
     public void onScroll(AbsListView view, float deltaY); 
    } 
} 
+2

Me hace preguntarme: ¿por qué los desarrolladores de Android no solo proporcionaron a los creadores de aplicaciones esa funcionalidad en la API? ¿Por qué no hacen que el método ScrollView onScrollChanged() sea público de forma predeterminada? – JakeP

+0

No tengo idea. Es realmente malo con lo que tenemos que trabajar, pero si cambian ahora, llegarían 5 años demasiado tarde. Tal vez lanzar algo en la biblioteca de soporte. De todos modos, solo para decirles que edité la respuesta con una solución aún mejor. – Budius

+0

Este es un gran fragmento de código; sin embargo, me gustaría preguntar: ¿es posible detectar la cantidad de sobredespliegue de listView utilizando el código ur? –

Cuestiones relacionadas