5

Sommario: Provare ad aggiungere dinamicamente righe di intestazione a un ListView tramite un wrapper adattatore personalizzato. ListView ha problemi a mantenere sincronizzata la posizione di scorrimento. Progetto dimostrativo eseguibile fornito.Come risolvere i problemi notifyDataSetChanged/ListView in dynamic Adapter wrapper Android

Vorrei aggiungere dinamicamente gli elementi a un elenco in base ai valori in un CursorAdapter, diverse posizioni rispetto a ciò che l'utente sta visualizzando attualmente. Per fare questo, ho un adattatore che avvolge il CursorAdapter e mantiene il nuovo contenuto indicizzato in uno Sparray. Il ListView deve essere aggiornato quando gli oggetti vengono aggiunti all'adattatore personalizzato, ma ho incontrato molte insidie ​​che cercano di farlo funzionare e mi piacerebbe un consiglio.

Il progetto demo può essere scaricato qui: DynamicSectionedList.zip

Nella demo, i titoli sono aggiunti in modo dinamico, cercando avanti di 10 posti per trovare la posizione corretta in cui le voci di elenco passare alla lettera successiva. Ogni implementazione di notifyDataSetChanged ha problemi come descritto:

Demo 1 Questa demo mostra l'importanza di notifyDataSetChanged(). Facendo clic su qualsiasi cosa, l'app si arresta in modo anomalo. Ciò è dovuto al controllo di integrità in ListView ... mItemCount != adapter.getItemCount(). Morale, dobbiamo notificare alla lista che i dati sono cambiati.

Demo 2 il naturale passo successivo è quello di informare il ListView di modifiche in caso di modifiche. Sfortunatamente, mentre si esegue lo scrolling di ListView, si interrompe in modo definitivo tutte le interazioni tattili fino a quando l'app non passa in modalità touch. Avrai bisogno di "fling scroll" abbastanza lontano da generare nuovi titoli per poterlo notare. Toccando lo schermo non si arresta lo scorrimento e, una volta interrotta, nessuna delle voci dell'elenco sarà selezionabile. Ciò è dovuto al codice if (!mDataChanged) { /* do very important stuff */ } in AbsListView.onTouchEvent().

Demo 3 Per risolvere questo problema, Demo 3 introduce una bandiera pendingChanges e gli utili adattatore personalizzato un notifyDataSetChangedIfNeeded(), che possono essere richiamate da ListView una volta che è entrato in uno stato "sicuro" per le modifiche. Il primo punto in cui le modifiche devono essere notificate è in ListView.layoutChildren(), quindi ho annullato tale metodo per la prima notifica delle modifiche, se necessario, quindi richiamato. Trascina almeno un'intestazione quindi fai clic su una voce dell'elenco.

Questo non funziona correttamente, anche se non ne sono del tutto sicuro. Toccando o selezionando un elemento con la tastiera/trackball, l'elenco si aggiorna senza sincronizzare correttamente la vecchia posizione. Scorre verso l'alto della lista che non è accettabile.

Demo 4 Il problema di scorrimento in Demo 3 può essere superato, almeno in modalità tocco. Aggiungendo una chiamata a notifyDataSetChangedIfNeeded() al tocco, la modifica dei dati avviene in un momento in cui tutte le interazioni touch funzionano come previsto e la posizione dell'elenco è correttamente sincronizzata.

Tuttavia, non riesco a trovare un analogo per questo quando il dispositivo non è in modalità touch, per non parlare del fatto che sembra decisamente un hack. L'elenco scorre quasi sempre verso l'alto, non riesco a scoprire cosa lo induce a mantenere di tanto in tanto la posizione corretta.

Poiché Android mi sta combattendo a ogni stadio del passaggio, sento che dovrebbe esserci un approccio migliore. Si prega di provare la demo, se possono essere applicate eventuali correzioni per farlo funzionare sarebbe fantastico!

Mille grazie a tutti coloro che possono esaminare questo aspetto, speriamo che se riusciremo a far funzionare il codice sarà utile per gli altri che cercano di ottenere la stessa ottimizzazione per gli elenchi con i titoli.

+1

I loop for immediatamente sotto i pulsanti 'LinearLayout' non sembrano avere una sintassi Java valida. Sembra inoltre che manchi qualsiasi codice che compila l'elenco. Ti consiglio di creare un progetto completo contenente questa roba, comprimerlo e collegarlo alla tua domanda. Anche in questo caso, non darei enormi speranze al tuo aiuto, dato che si tratta di un considerevole pezzo di codice difficile da decifrare, e in realtà non stai ponendo alcuna domanda nella tua domanda. – CommonsWare

+1

Si potrebbe prendere in considerazione l'avvio di una seconda domanda, espandendo il problema reale ("la quantità di dati nell'elenco può essere piuttosto grande e il calcolo delle intestazioni può essere considerato abbastanza costoso da richiedere l'ottimizzazione"). Quello che hai qui è qualcosa in qualche modo collegato al tuo codice reale che tenta di affrontare il vero problema. Tuttavia, potrebbe essere più semplice per voi ottenere aiuto con il vero problema stesso, capendo perché "il calcolo delle intestazioni" è "costoso" e come affrontarlo. Naturalmente, lo chiedi anche questo giovedì prima di un fine settimana festivo ... – CommonsWare

+2

BTW, FWIW, ho scritto un post sul blog con le mie idee sull'aggiunta di titoli a un 'CursorAdapter': http://commonsware.com/ blog/2010/12/30/adding-headings-cursoradapter.html – CommonsWare

risposta

4

Il problema principale con l'approccio presentato sopra era che il set di dati veniva modificato direttamente dal codice eseguito dalle superclassi. Compilando i titoli in getView() stavo cambiando i dati in quello che potrebbe essere il centro di un'operazione o uno stato "non sicuro" nelle superclassi. Questo è il motivo per cui tutte le interazioni tattili si sono interrotte quando onNotifyDataSetChanged è stato chiamato durante uno scrolling. I dati devono essere aggiunti da una fonte esterna, non dai metodi dell'adattatore.

Ciò può essere fatto utilizzando un gestore o AsyncTask per aggiungere le intestazioni all'adattatore e chiamare il suo notifyDataSetChanged. Questi verranno eseguiti su un thread dell'interfaccia utente e non interferiranno con lo stato della superclasse. Grazie a CommonsWare's EndlessAdapter esempio per introdurre il concetto di AsyncTask per aggiungere dati a un elenco.

Gli hack onTouchEvent() e layoutChildren() non erano più necessari dopo aver apportato tale modifica.

Problemi correlati