2010-12-13 16 views
16

Estoy creando una lista de imágenes usando un ListView y las fotos son de un tamaño que caben de 2 a 3 fotos en la pantalla.Ajustar vista de lista al elemento

El problema que estoy teniendo es que me gustaría cuando el usuario deja de desplazarse que el primer elemento de la lista visible se ajustaría a la parte superior de la pantalla, por ejemplo, si el desplazamiento termina y una pequeña parte del se muestra la primera imagen, desplazamos la lista hacia abajo para que la imagen siempre se muestre por completo, si la mayoría de las imágenes se muestran, desplazamos la lista hacia arriba para que la siguiente imagen sea completamente visible.

¿Hay alguna manera de lograr esto en Android con la vista de lista?

Respuesta

26

he encontrado una manera de hacer esto sólo escuchar para desplazarse y cambiar la posición cuando el rollo terminado mediante la implementación de ListView.OnScrollListener

@Override 
public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     if (scrolling){ 
      // get first visible item 
      View itemView = view.getChildAt(0); 
      int top = Math.abs(itemView.getTop()); // top is a negative value 
      int bottom = Math.abs(itemView.getBottom()); 
      if (top >= bottom){ 
       ((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition()+1, 0); 
      } else { 
       ((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition(), 0); 
      } 
     } 
     scrolling = false; 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
    case OnScrollListener.SCROLL_STATE_FLING: 
     Log.i("TEST", "SCROLLING"); 
     scrolling = true; 
     break; 
    } 
} 

El cambio no es tan suave, pero funciona.

+0

Si quiere que sea suave intente usar (vista (ListView)).smoothScrollToPosition (pos); – stealthcopter

+2

Dos problemas para hacer eso: 1) smoothScrollToPosition hará que la lógica anterior detecte otro desplazamiento y entre en un bucle nervioso. Puede evitar esto quitando el estuche para SCROLL_STATE_FLING, pero no estoy seguro de si es una buena solución. 2) smoothScrollToPosition no alinea las cosas correctamente, al menos con mis listas. Desplaza la parte superior del elemento especificado a tal vez 15 píxeles por encima de la vista de lista, en lugar de alinear las cosas correctamente como setSelection/setSelectionFromTop do. – benkc

+0

¿Alguna solución suave? – benkol

0

Además de probar el código de arriba, una cosa que debe asegurarse es que su lista tenga una altura que se ajuste al número exacto de elementos que desea que se muestren. por ejemplo Si desea que se muestren 4 elementos después del efecto de ajuste y la altura de la fila (definida en su diseño) debe ser 1/4 de la altura total de la lista.

2

Utilizando la solución @nininho 'S,

En el onScrollStateChanged cuando el estado cambia a SCROLL_STATE_IDLE, recuerde la posición para tomar y levantar una bandera:

snapTo = view.getFirstVisiblePosition(); 
shouldSnap = true; 

A continuación, reemplazar el método computeScroll():

@Override 
public void computeScroll() { 
    super.computeScroll(); 
    if(shouldSnap){ 
     this.smoothScrollToPositionFromTop(snapTo, 0); 
     shouldSnap = false; 
    } 
} 
5

Utilizando un par de ideas de la solución de @ nininho, obtuve mi lista para ajustar al elemento con un scr suave oll en lugar de ir abruptamente a ella. Una advertencia es que solo probé esta solución en un Moto X en un ListView básico con texto, pero funciona muy bien en el dispositivo. Sin embargo, confío en esta solución y lo animo a que envíe sus comentarios.

listview.setOnScrollListener(new OnScrollListener() { 
     @Override 
     public void onScrollStateChanged(AbsListView view, int scrollState) { 
      // TODO Auto-generated method stub 
      if (scrollState == SCROLL_STATE_IDLE) { 
       View itemView = view.getChildAt(0); 
       int top = Math.abs(itemView.getTop()); 
       int bottom = Math.abs(itemView.getBottom()); 
       int scrollBy = top >= bottom ? bottom : -top; 
       if (scrollBy == 0) { 
        return; 
       } 
       smoothScrollDeferred(scrollBy, (ListView)view); 
      } 
     } 

     private void smoothScrollDeferred(final int scrollByF, 
       final ListView viewF) { 
      final Handler h = new Handler(); 
      h.post(new Runnable() { 

       @Override 
       public void run() { 
        // TODO Auto-generated method stub 
        viewF.smoothScrollBy(scrollByF, 200); 
       } 
      }); 
     } 

     @Override 
     public void onScroll(AbsListView view, int firstVisibleItem, 
       int visibleItemCount, int totalItemCount) { 
      // TODO Auto-generated method stub 

     } 
    }); 

La razón por la que diferir el desplazamiento suave se debe a que en mis pruebas, llamar directamente al método smoothScrollBy en el estado cambia de devolución de llamada había problemas en realidad el desplazamiento. Además, no preveo que una solución robusta y completamente probada tenga mucho estado, y en mi solución a continuación, no tengo ningún estado. Esta solución aún no está en Google Play Store, pero debería servir como un buen punto de partida.

+0

Cuando 'top> = bottom',' scrollBy' es igual a (** N: ** cols en GridView, N = 1 para ListView) 'view.getChildAt (view.getFirstVisiblePosition() + N) .getY()'. – samosaris

0

Tenga en cuenta que después de la llamada smoothScrollBy(), getFirstVisiblePosition() puede apuntar al elemento de la lista por encima de la más alta en la vista de lista. Esto es especialmente cierto cuando view.getChildAt(0).getBottom() == 0. Tuve que llamar al view.setSelection(view.getFirstVisiblePosition() + 1) para remediar este extraño comportamiento.

Cuestiones relacionadas