42

Sto provando a scorrere senza problemi fino all'ultimo elemento di una lista dopo aver aggiunto un elemento all'arrayadapter associato a listview. Il problema è che solo scorre in una posizione casualeandroid: smoothScrollToPosition() non funziona correttamente

arrayadapter.add(item); 
//DOES NOT WORK CORRECTLY: 
listview.smoothScrollToPosition(arrayadapter.getCount()-1); 

//WORKS JUST FINE: 
listview.setSelection(arrayadapter.getCount()-1); 
+1

e questo metodo non ha mai funzionato per me. –

+2

Bug noto: vedere la risposta https://stackoverflow.com/questions/14479078/smoothscrolltopositionfromtop-is-not-always-working-like-it-should/20997828#20997828 –

risposta

1

Ti chiami arrayadapter.notifyDataSetChanged() dopo aver chiamato arrayadapter.add()? Anche per essere sicuri, smoothScrollToPosition e setSelection sono metodi disponibili in ListView non arrayadapter come si è menzionato sopra.

In ogni caso vediamo se questo aiuta: smoothScrollToPosition after notifyDataSetChanged not working in android

+0

sì .. Ho fatto un errore qui nel forum, è ovviamente listview.smoothScrollToPosition (pos) e listview.setSelection (pos). e notifyDataSetChanged vengono richiamati automaticamente quando si chiama arrayadapter.add() .., altrimenti setSelection non funzionerebbe – user1515520

+1

è strano che setSelection (pos) funzioni perfettamente, ma smoothScrollToPosition (pos) no, non è vero? – user1515520

5

Si dovrebbe utilizzare il metodo setSelection().

+2

Non è necessariamente la stessa cosa, potresti non volere che l'oggetto sia selezionato, o puoi semplicemente cercare l'effetto liscio – mdelolmo

+0

Mi ha effettivamente aiutato, invece che non succede nulla, continua a scorrere fino alla fine dell'elenco. – Slickelito

+1

'setSelection' funziona davvero dove' smoothScrollToPosition' non funziona, ma sarebbe utile se puoi illuminarci sul perché sia ​​così. –

51

Probabilmente vuoi dire a ListView di postare lo scroll quando il thread dell'interfaccia utente può gestirlo (motivo per cui il tuo non scorre correttamente). SmoothScroll deve fare molto lavoro, invece di andare in una posizione che ignora velocità/tempo/ecc. (richiesto per una "animazione").

Pertanto si dovrebbe fare qualcosa di simile:

getListView().post(new Runnable() { 
     @Override 
     public void run() { 
      getListView().smoothScrollToPosition(pos); 
     } 
    }); 
+13

+1 per l'idea di 'post'. Tuttavia, mi sono imbattuto in casi in cui 'smoothScrollToPosition' non funziona ancora, lasciando un piccolo spazio di offset tra la parte superiore del widget e l'elemento nella posizione in cui scorrere. In questi casi, 'setSelection' (combinato con' post', ovviamente) funziona perfettamente. –

+0

Sì, anche a me. Le ListView sono di gran lunga inferiori a UITableView e simili. È triste, perché le Liste sono un concetto fondamentale nel mobile in cui le dimensioni dello schermo sono compromesse, quindi dobbiamo scorrere le cose. In ogni caso, solo per quello che sai, lo scorrimento funziona, devi solo capire le regole, il layout deve essere stato posto, devi farlo nel thread dell'interfaccia utente, devi postarlo in modo che l'elenco possa accodare l'operazione, ecc. Un sacco di magia deve accadere affinché la pergamena si verifichi. Sono sicuro che @RomanianGuy possa gettare più luce in questo. Tuttavia, l'implementazione di iSO funziona in modo impeccabile per la maggior parte. –

+5

Il nome del ragazzo è @RomainGuy BTW. –

11

(copiato da mia risposta: smoothScrollToPositionFromTop() is not always working like it should)

Questo è un bug noto. Vedere https://code.google.com/p/android/issues/detail?id=36062

Tuttavia, ho implementato questa soluzione che si occupa di tutti i casi limite che potrebbero verificarsi:

Prima chiamata smothScrollToPositionFromTop(position) e poi, quando ha finito di scorrimento, chiamare setSelection(position). Quest'ultima chiamata corregge lo scorrimento incompleto saltando direttamente nella posizione desiderata. In questo modo, l'utente ha ancora l'impressione di scorrere l'animazione verso questa posizione.

ho implementato questa soluzione entro due metodi di supporto:

smoothScrollToPosition()

public static void smoothScrollToPosition(final AbsListView view, final int position) { 
    View child = getChildAtPosition(view, position); 
    // There's no need to scroll if child is already at top or view is already scrolled to its end 
    if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) { 
     return; 
    } 

    view.setOnScrollListener(new AbsListView.OnScrollListener() { 
     @Override 
     public void onScrollStateChanged(final AbsListView view, final int scrollState) { 
      if (scrollState == SCROLL_STATE_IDLE) { 
       view.setOnScrollListener(null); 

       // Fix for scrolling bug 
       new Handler().post(new Runnable() { 
        @Override 
        public void run() { 
         view.setSelection(position); 
        } 
       }); 
      } 
     } 

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

    // Perform scrolling to position 
    new Handler().post(new Runnable() { 
     @Override 
     public void run() { 
      view.smoothScrollToPositionFromTop(position, 0); 
     } 
    }); 
} 

getChildAtPosition()

public static View getChildAtPosition(final AdapterView view, final int position) { 
    final int index = position - view.getFirstVisiblePosition(); 
    if ((index >= 0) && (index < view.getChildCount())) { 
     return view.getChildAt(index); 
    } else { 
     return null; 
    } 
} 
+0

Questa è la risposta corretta, grazie! – FireZenk

2

Il metodo di selezione set citato da Lars funziona, ma l'animazione era troppo nervosa per i nostri scopi mentre salta tutto ciò che è rimasto. Un'altra soluzione è quella di richiamare il metodo ripetutamente finché la prima posizione visibile non è il tuo indice. Questo è meglio fatto rapidamente e con un limite in quanto combatterà l'utente scorrendo la vista altrimenti.

private void DeterminedScrollTo(Android.Widget.ListView listView, int index, int attempts = 0) { 
    if (listView.FirstVisiblePosition != index && attempts < 10) { 
     attempts++; 
     listView.SmoothScrollToPositionFromTop (index, 1, 100); 
     listView.PostDelayed (() => { 
      DeterminedScrollTo (listView, index, attempts); 
     }, 100); 
    } 
} 

Soluzione in C# via. Xamarin, ma dovrebbe tradursi facilmente in Java.

1
final Handler handler = new Handler(); 
//100ms wait to scroll to item after applying changes 
handler.postDelayed(new Runnable() { 
@Override 
public void run() { 
    listView.smoothScrollToPosition(selectedPosition); 
}}, 100); 
Problemi correlati