2009-12-31 13 views
43

Ho un Android ListActivity supportato da un database Cursor tramite un SimpleCursorAdapter.Android SimpleCursorAdapter non si aggiorna quando le modifiche del database

Quando si fa clic sugli elementi, viene attivato un campo flag nella riga corrispondente nel database e la vista nell'elenco deve essere aggiornata.

Il problema è che quando la vista che viene aggiornata si spegne e viene riciclata, il vecchio valore viene visualizzato sulla vista quando ritorna in visualizzazione. La stessa cosa accade ogni volta che la lista è ridisegnata (cambiamenti di orientamento, ecc.).

Io uso notifydatasetchanged() per aggiornare l'adattatore del cursore ma sembra inefficace.

Come devo aggiornare il database in modo che anche il cursore venga aggiornato?

risposta

89

chiamata requery() sul Cursor quando si cambia i dati nel database che si desidera riflette in quello Cursor (o cose le Cursor popola, come un ListView tramite un CursorAdapter).

A Cursor è simile a un cursore lato client ODBC: contiene tutti i dati rappresentati dal risultato della query. Quindi, solo perché si modificano i dati nel database, lo Cursor non sarà a conoscenza di tali modifiche, a meno che non lo si aggiorni tramite requery().


UPDATE: L'intera questione e una serie di risposte dovrebbero essere cancellati a causa della vecchiaia, ma questo è evidentemente impossibile. Chiunque cerchi risposte per Android dovrebbe tenere a mente che l'Android è un obiettivo in rapida evoluzione e le risposte del 2009 sono in genere peggiori delle risposte più recenti.

La soluzione attuale è quella di ottenere un fresco Cursor e utilizzare changeCursor() o swapCursor() sulla CursorAdapter di influenzare una modifica dei dati.

+0

Così che cosa notifydatachanged fare allora? – CodeFusionMobile

+11

Non c'è "notifydatachanged". Se si intende notifyDataSetChanged() su Adapter, è così che SimpleCursorAdapter comunica a ListView che i dati sono stati modificati. Per citare dalla documentazione, "Notifica alla vista allegata che i dati sottostanti sono stati cambiati e dovrebbe aggiornarsi." Tuttavia, il problema non è con l'adattatore che dice a ListView della modifica - il tuo problema è che l'adattatore non sa che i dati sono cambiati. Calling Requery() è il modo per affrontarlo con un CursorAdapter. – CommonsWare

+0

Questo ha senso ora. Ho frainteso il flusso di dati. – CodeFusionMobile

37

requery è ora obsoleto. da documentation:

Questo metodo è obsoleto. Non usare questo. Basta richiedere un nuovo cursore, in modo da poterlo fare in modo asincrono e aggiornare la visualizzazione elenco una volta che il nuovo cursore è tornato.

dopo aver ottenuto un nuovo cursore si può usare il adapter.changeCursor(cursor). questo dovrebbe aggiornare la vista.

+0

Grazie per le informazioni, ho avuto un crash su Honeycomb relativo a cursor.requery(), forse questo è dietro. – CodeFusionMobile

+0

La situazione comune è quella di modificare il valore da onClickListener() Ciò che mi indovina è che da un lato, stiamo inviando il cursore iniziale attraverso il costruttore (cioè la query è scritta all'esterno dell'Adpater) Ma d'altra parte, l'onlickLister viene definito come classe nidificata all'interno dell'adattatore. Quindi la query per il nuovo cursore verrebbe scritta all'interno della classe dell'adattatore. Ciò potrebbe causare un codice duplicato. Qual è il progetto giusto per scrivere query solo una volta nel codice sorgente? –

20

In caso di utilizzo del caricatore e del cursore automagicamente generato è possibile chiamare:

getLoaderManager().restartLoader(0, null, this); 

nella vostra attività, solo dopo aver cambiato qualcosa su un DB, per rigenerare nuovo cursore. Non dimenticare di avere anche gestori di eventi definiti:

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    CursorLoader cursorLoader = 
      new CursorLoader(this, 
        YOUR_URI, 
        YOUR_PROJECTION, null, null, null); 
    return cursorLoader; 
} 

@Override 
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
    adapter.swapCursor(data); 
} 

@Override 
public void onLoaderReset(Loader<Cursor> loader) { 
    adapter.swapCursor(null); 
} 
+2

Questa è una buona soluzione. Grazie per questa risposta. – Vincent

+2

Ho provato una mezza dozzina di cose diverse da Stack Overflow e questa è stata la soluzione che finalmente ha funzionato per me. Grazie! – Rudism

+0

Questa è esattamente la risposta che stavo cercando, usando un semplice adattatore, e chiamando swapCursor su LoadFinished. – Matt

1

non mi è chiaro se si imposta la proprietà autoRequery di CursorAdapter a true.

L'adattatore controlla la proprietà autoRequery; se è false, il cursore non verrà modificato.

+0

'protected void init (Contesto contesto, Cursore c, autoRequery booleano)' * Questo metodo è deprecato. Non utilizzare questo, utilizzare il costruttore normale. Questo sarà rimosso in futuro. * – Mussa

0

Requery() è già deprecato, basta implementare il metodo semplice updateUI() come questo in classe figlia del tuo CursorAdapter e chiamarlo dopo gli aggiornamenti dei dati:

private void updateUI(){ 
    swapCursor(dbHelper.getCursor()); 
    notifyDataSetChanged(); 
} 
0

E 'facile.

private Db mDbAdapter; 
private Cursor mCursor; 
private SimpleCursorAdapter mCursorAd; 

..................................... 
//After removing the item from the DB, use this 
..................................... 

mCursor = mDbAdapter.getAllItems(); 
mCursorAd.swapCursor(mCursor); 

Oppure utilizzare CursorLoader ...

Problemi correlati