2012-03-15 14 views
9

Sviluppatore Android abbastanza nuovo qui.getView chiamato con posizione errata durante lo scorrimento veloce

Mi sono imbattuto in uno strano problema che non so come risolvere. Ho letto molti problemi qui intorno che sembrano lo stesso problema che ho, ma le soluzioni ai loro problemi non sembravano mai applicabili a ciò che sta accadendo qui.

Ho un listView personalizzato impostato utilizzando una classe separata che estende BaseAdapter e utilizza un titolare della vista (ho impostato questo aspetto guardando vari diversi esempi di visualizzazioni di elenco personalizzate). Ha una visualizzazione testuale, un pulsante, una barra di avanzamento e un pulsante immagine, e questi elementi sono nascosti/modificati in base a un AsyncTask utilizzato per scaricare un file.

Tutto funziona correttamente quando faccio scorrere l'elenco lentamente. Quando faccio scorrere rapidamente l'elenco su/giù, è come se le viste di alcune celle nell'elenco venissero riassegnate ad altre celle.

ho messo questa linea nella parte superiore del mio metodo "GetView" nella mia classe di adattatori:

@Override 
    public View getView(final int pos, View convertView, ViewGroup parent) 
    { 
     Log.i("ks3","getView called, position is " + pos); 

ho 7 elementi nella mia lista, e 6 di loro stanno sullo schermo. Quando prima viene visualizzata, vedo questo nel mio ceppo:

getView called, position is 0 
getView called, position is 1 
getView called, position is 2 
getView called, position is 3 
getView called, position is 4 
getView called, position is 5 

Quando ho scorrere verso il basso lentamente alla voce successiva, questo viene stampato prossimo:

getView called, position is 6 

Quando ho scorrere indietro in su, questo :

Il basso e il basso producono questi risultati in modo impeccabile.

Quando comincio lo scorrimento avanti e indietro velocemente, esso stampa questo messaggio un mucchio di volte, che mostra la maggior parte 6 e 0 come la posizione, ma di tanto in tanto mi vedrà questi così:

getView called, position is 1 
getView called, position is 5 

posizioni 1 e 5 non dovrebbero essere chiamati, dato che sono sempre sullo schermo. È come se getView si fosse fatto tutto confuso. Allo stesso tempo, come ho detto prima, la mia lista sembrerà strana. I pulsanti immagine verranno spostati verso l'alto o verso il basso in una cella in cui non dovrebbero essere.

Se torno indietro a scorrere senza problemi, vedo solo 0 e 6 per la posizione di nuovo.

Sinceramente non sono sicuro di come aggirare questo problema. Ho pensato che forse potrei limitare la velocità di scorrimento della lista, ma non sono riuscito a trovare nulla che funzioni.

Grazie!

Modifica: Volevo aggiornare un paio di cose riguardo a questa domanda. Il primo è che, da un commento qui e dal video di Google su listView, è venuto alla mia attenzione che getView può essere chiamato per altre cose quindi quello che immaginavo fosse, come le misurazioni, e quindi non dovrei essere allarmato da quello che pensavo originariamente faceva parte del mio problema (essendo che pensavo che getView venisse chiamato con le posizioni sbagliate).

In secondo luogo, ho visto più volte che è una pessima idea "memorizzare le viste nell'adattatore". Non sono esattamente chiaro su cosa significhi, ma sono abbastanza sicuro che sia una delle cose che sto facendo male (ho salvato un'istanza di un pulsante, progressBar, imageButton ...).

Questo, insieme al fatto che aggiorno le viste al di fuori di getView e non uso notifyDataSetChanged(); quelli insieme stanno probabilmente facendo succedere cose strabilianti.

+0

A meno che il tuo ListView personalizzato non stia facendo qualcosa di funky, sembra un problema di riciclaggio, inserisci il tuo codice getView() completo. – dmon

+0

Se non si riscontrano errori nel codice e si utilizza Honeycomb o versione successiva, è possibile provare a disabilitare l'accelerazione hardware per il layout dell'elenco. L'accelerazione hardware può potenzialmente causare tali errori. – zapl

+1

Sembra un problema di riciclaggio. Ho intenzione di dare un'occhiata al video di Google su listViews e vedere se ci sono problemi evidenti con come sto usando listView. – Droidmon

risposta

13

È corretto che ListView stia riutilizzando le viste in diversi punti sullo schermo. È un'ottimizzazione per mantenere l'uso della memoria ragionevole e veloce non assegnando continuamente nuove visualizzazioni.

È probabile che tu stia utilizzando LiewView in modo errato. Guarda this talk on how to properly use ListView per ottenere l'intera storia, ma ecco i punti salienti:

  1. "Posizione" si riferisce alla posizione dei dati nell'elenco di schede. Se i dati dell'adattatore sono in un array, questo sarebbe l'indice in quell'array.
  2. "ID" si riferisce al valore dei dati stessi. Se hai un elenco di nomi e li ricorri, la loro posizione cambierà, ma il loro ID non lo farà.
  3. "Indice" si riferisce alla posizione relativa di una vista rispetto all'area dello schermo visualizzabile. Probabilmente non ne avrai mai bisogno.
  4. Non manipolare le viste (o tentare di memorizzarle nella cache) al di fuori del metodo getView() della scheda o si otterrà uno strano comportamento.
  5. Se la chiamata a getView(int, View, ViewGroup) fornisce un'istanza di visualizzazione, compila i suoi campi anziché gonfiare completamente nuove visualizzazioni. Supponendo che tu abbia implementato correttamente getItemType(), avrai sempre il giusto tipo View da ripopolare.
  6. Effettua il tuo metodo getView() il più velocemente possibile e fai solo un sollevamento pesante su altri fili.
  7. Solo perché il contenitore chiamato getView() non significa necessariamente che i dati verranno visualizzati. Il framework usa questi per scopi di misurazione. Dal momento che il lavoro potrebbe essere buttato via, questo è un altro motivo per assicurarsi che il numero getView() sia il più veloce possibile.
  8. Quando succede qualcosa ai tuoi dati che devi mostrare sullo schermo, dì che il tuo download è completo, cioè quando chiami notifyDataSetChanged(). Non giocherellare direttamente con le visualizzazioni, verranno popolate nel successivo ciclo dell'interfaccia utente quando viene ridisegnato.

Avendo appena trascorso alcuni giorni a rielaborare uno ListView implementato in modo ingenuo, sento il tuo dolore. I risultati sono valsi la pena, però!

+2

Argyle, grazie mille per il link. Non ne so abbastanza su come funziona listView, e ammetto che sto aggiornando le viste al di fuori di getView, quindi dovrò capire come lavorare con notifyDataSetChanged(). – Droidmon

+0

Sir pls sentire il mio dolore! Puoi rispondere a questa domanda http://stackoverflow.com/questions/22451081/how-to-track-the-position-of-item-while-scrolling-in-a-long-listview –

+0

Ho qualche medio pesante elaborare e aggiungere una forma dinamica su ogni oggetto e trovo il posto giusto in getview(). riguardo al punto in cui posso spostarlo? – Kenji

0

getView viene chiamato per ogni voce di elenco che deve essere disegnata ma precedentemente non era visibile sullo schermo. Se scorri velocemente potresti ottenere posizioni strane, ma ciò non dovrebbe causare errori come descrivi (suppongo che Android non abbia alcun errore qui dal momento che quelle ListViews sono abbastanza ben testate). Per esempio. quando scorri abbastanza velocemente potresti avere 2 punti di vista che devono essere disegnati. Forse c'è qualcos'altro che non va nel tuo codice.

+0

Ah, ok, questo è bello sapere, sono contento che non lo sia, o almeno non dovrebbe essere ciò che sta causando il problema. – Droidmon

4

utilizzare sempre questo approccio in getview: convertView = inflater.inflate (R.layout.listinflate, parent, false);

E lascia che la tua attività implementa onScrollListener e quando l'utente sta scoccando notifica l'adattatore listview per non preoccuparti di impegnarsi ad aggiornare correttamente gli articoli. e quando l'utente non è più in azione, dì all'adattatore di occuparsi della fornitura dei dati in getView.

Questo ha risolto tutti i miei problemi. E un'altra cosa non usare wrap_content nell'altezza della visualizzazione elenco.imposta anche smoothscroll false.

+0

non usare wrap_content mi ha aiutato – pkacprzak

1

Ho avuto lo stesso problema: nell'adattatore stavo cambiando il colore di sfondo nel metodo getView ma a volte la listview era vista "reciclyng" (ottenendo lo sfondo sbagliato). ho risolto semplicemente mettendo "altro" per ogni

if(...){changeBackground}

ho aggiunto

else {restore default background}

E poi ha funzionato senza intoppi

+0

Uomo, sei fantastico! Ho passato una buona parte della mattinata a capire questo problema. Molte grazie! –

+0

nessun problema! Felice di sentirlo! ;) – Mark

0

Per la cronaca ed estendendo la risposta di @ Marco

Android fa il prossimo compito. Diciamo che ho una lista con 100 elementi. Mentre stai caricando, getview viene chiamato per l'elemento da 0,1,2,3 ... a 10 per esempio. E se continuiamo, la prossima posizione sarà la 11. Tuttavia, la posizione 11 ricicla la stessa vista rispetto alla posizione 0. Quindi, la vista per la posizione 11 esiste ma ha il valore sbagliato (il valore 0).

Risposta: modificare i dati se la vista è nullo o no. Se la vista è nullo, sgonfia e modifica i valori della vista. In caso contrario, modifica i valori della vista.

Diciamo che ho un item_view (layout) con un singolo TextView, l'adattatore dovrebbe essere come segue:

Versione destra:

public class XXXAdapter extends ArrayAdapter<XXX> {.... 
@NonNull 
@Override 
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { 
    if (convertView==null) { 
     LayoutInflater inflater = LayoutInflater.from(this.getContext()); 
     convertView = inflater.inflate(R.layout.**itemofthelayout**, parent, false); 
    } 
    ***Object** item=this.getItem(position); 
    TextView txt= (TextView) convertView.findViewById(R.id.**idsometextviewinsidelayout**); 
    txt.setText(String.valueOf(position)); 
    return convertView; 
    // return super.getView(position, convertView, parent); 
} 

Versione sbagliata:

public class XXXAdapter extends ArrayAdapter<XXX> {.... 
@NonNull 
@Override 
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { 
    if (convertView==null) { 
     LayoutInflater inflater = LayoutInflater.from(this.getContext()); 
     convertView = inflater.inflate(R.layout.**itemofthelayout**, parent, false); 
     ***Object** item=this.getItem(position); 
     TextView txt= (TextView) convertView.findViewById(R.id.**idsometextviewinsidelayout**); 
     txt.setText(String.valueOf(position)); 
     return convertView; 
     // return super.getView(position, convertView, parent); 
    } 
} 
Problemi correlati