2010-12-13 18 views
16

Sto creando un elenco di immagini utilizzando un controllo ListView e le foto sono di una dimensione che potrebbe contenere da 2 a 3 foto sullo schermo.Visualizzazione elenco snap all'elemento

Il problema che sto avendo è che mi piacerebbe quando l'utente smette di scorrere che il primo elemento dell'elenco visibile si agganci alla parte superiore dello schermo, ad esempio, se lo scorrimento termina e una piccola parte del prima immagine visualizzata, scorriamo la lista verso il basso in modo che l'immagine sia sempre completamente visualizzata, se la maggior parte dell'immagine viene visualizzata, scorriamo l'elenco verso l'alto in modo che l'immagine successiva sia completamente visibile.

C'è un modo per ottenere questo in Android con listview?

risposta

26

ho trovato un modo per fare questo basta ascoltare per scorrere e cambiare la posizione in cui il rotolo finito mediante l'attuazione di 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; 
    } 
} 

Il cambiamento non è così liscia ma funziona.

+0

Se si desidera che sia fluido, provare a utilizzare (vista (Visualizzazione elenco)).smoothScrollToPosition (pos); – stealthcopter

+2

Due problemi: 1) smoothScrollToPosition farà in modo che la logica sopra descritta rilevi un'altra pergamena e vada in un loop nervoso: puoi evitarlo rimuovendo la custodia per SCROLL_STATE_FLING ma non sono sicuro che sia una buona soluzione. 2) smoothScrollToPosition non allinea le cose correttamente, almeno con le mie liste. Fa scorrere la parte superiore dell'elemento specificato fino a 15 pixel sopra la cima della listview, invece di allineare le cose correttamente come setSelection/setSelectionFromTop do. – benkc

+0

Qualsiasi soluzione liscia? – benkol

0

Oltre a provare il codice sopra una cosa che dovresti assicurarti è che il tuo listView abbia un'altezza adatta al numero esatto di elementi che vuoi visualizzare. ad esempio Se si desidera visualizzare 4 elementi dopo l'effetto snap e l'altezza della riga (definita nel layout) deve essere 1/4 dell'altezza totale dell'elenco.

2

Utilizzando la soluzione @nininho 's,

Nel onScrollStateChanged quando cambia lo stato di SCROLL_STATE_IDLE, ricordare la posizione per scattare e alzare una bandiera:

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

Poi, l'override del metodo computeScroll():

5

Utilizzando un paio di idee dalla soluzione di @ nininho, ho ottenuto il mio listview per eseguire lo snap all'elemento con un semplice scr invece di andarci bruscamente. Un avvertimento è che ho testato questa soluzione solo su un Moto X in un ListView di base con testo, ma funziona molto bene sul dispositivo. Tuttavia, sono fiducioso su questa soluzione e ti incoraggio a fornire feedback.

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 

     } 
    }); 

Il motivo per cui rimandare l'scorrimento continuo è perché nel mio test, chiamando direttamente il metodo smoothScrollBy nello stato cambiato richiamata aveva problemi in realtà lo scorrimento. Inoltre, non prevedo una soluzione robusta e pienamente testata che tenga molto lo stato, e nella mia soluzione di seguito, non reggo affatto. Questa soluzione non è ancora disponibile nel Google Play Store, ma dovrebbe fungere da buon punto di partenza.

+0

Quando 'top> = bottom',' scrollBy' è uguale a (** N: ** cols in GridView, N = 1 per ListView) 'view.getChildAt (view.getFirstVisiblePosition() + N) .getY()'. – samosaris

0

noti che dopo la chiamata smoothScrollBy(), getFirstVisiblePosition() possono puntare alla voce lista di cui sopra quella più in alto nella ListView. Questo è particolarmente vero quando view.getChildAt(0).getBottom() == 0. Ho dovuto chiamare view.setSelection(view.getFirstVisiblePosition() + 1) per rimediare a questo strano comportamento.

Problemi correlati