2009-12-31 7 views
43

Tengo un Android ListActivity que está respaldado por una base de datos Cursor a través de SimpleCursorAdapter.Android SimpleCursorAdapter no se actualiza cuando la base de datos cambia

Cuando se hace clic en los elementos, se alterna un campo de bandera en la fila correspondiente en la base de datos y la vista en la lista debe actualizarse.

El problema es que cuando la vista que se actualiza se sale de la pantalla y se recicla, el valor anterior se muestra en la vista cuando vuelve a la vista. Lo mismo ocurre siempre que la lista se redibuja (la orientación cambia, etc.).

Uso notifydatasetchanged() para actualizar el adaptador del cursor, pero parece ineficaz.

¿Cómo debo actualizar la base de datos para que el cursor se actualice también?

Respuesta

89

llamada requery() en el Cursor cuando cambie los datos en la base de datos que desea que se refleja en esa Cursor (o cosas las Cursor puebla, como un ListView a través de un CursorAdapter).

A Cursor es similar a un cursor ODBC del lado del cliente: contiene todos los datos representados por el resultado de la consulta. Por lo tanto, solo porque cambie los datos en la base de datos, el Cursor no sabrá sobre esos cambios a menos que los actualice a través del requery().


ACTUALIZACIÓN: toda esta cuestión y un conjunto de respuestas deberían suprimirse debido a la vejez, pero eso es aparentemente imposible. Cualquiera que busque respuestas de Android debe tener en cuenta que Android es un objetivo que se mueve rápidamente, y las respuestas de 2009 suelen ser peores que las respuestas más recientes.

La solución actual es la obtención de un fresco Cursor y utilizar cualquiera changeCursor() o swapCursor() en la CursorAdapter para afectar un cambio de datos.

+0

Entonces, ¿qué hacer entonces notifydatachanged? – CodeFusionMobile

+11

No hay "notificación modificada". Si se refiere a notifyDataSetChanged() en Adapter, así es como SimpleCursorAdapter le dice a ListView que los datos fueron cambiados. Para citar la documentación, "Notifica a la Vista adjunta que los datos subyacentes se han cambiado y que debe actualizarse a sí mismo". Sin embargo, su problema no es que el Adaptador le haya informado al ListView sobre el cambio: su problema es que el Adaptador no sabe que los datos han cambiado. Llamar a requery() es la forma de abordar eso con un CursorAdapter. – CommonsWare

+0

Eso tiene sentido ahora. No entendí el flujo de datos. – CodeFusionMobile

37

requery ahora está en desuso. desde documentation:

Este método está en desuso. No use esto. Solo solicite un cursor nuevo, para que pueda hacerlo de forma asincrónica y actualice su vista de lista una vez que el cursor nuevo regrese.

después de obtener un nuevo cursor se puede usar el adapter.changeCursor(cursor). esto debería actualizar la vista.

+0

Gracias por la información, he estado teniendo un bloqueo en Honeycomb relacionado con cursor.requery(), quizás esto está detrás de esto. – CodeFusionMobile

+0

La situación común es cambiar el valor de onClickListener() Lo que me desconcierta es que, por un lado, estamos enviando cursor inicial a través del constructor (es decir, la consulta está escrita fuera del Adpater) Pero, por otro lado, onlickLister se define como clase anidada dentro del adaptador. Por lo tanto, la consulta del nuevo cursor se escribiría dentro de la clase de adaptador. Esto puede causar código duplicado. ¿Cuál es el diseño correcto para escribir una consulta solo una vez en el código fuente? –

20

En caso de utilizar cargador y el cursor generado automágicamente puede llamar:

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

en su actividad, justo después de cambiar algo en una base de datos, para regenerar nuevo cursor. no se olvide de contar también con los controladores de eventos definidos:

@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

Esa es una buena solución. Gracias por esta respuesta – Vincent

+2

Probé media docena de cosas diferentes alrededor de Stack Overflow y esta fue la que finalmente funcionó para mí. ¡Gracias! – Rudism

+0

Esta es exactamente la respuesta que estaba buscando, usando un adaptador simple y llamando a swapCursor onLoadFinished. – Matt

1

no me queda claro si se establece la propiedad de autoRequery CursorAdapter a true.

El adaptador comprobará la propiedad autoRequery; si es false, entonces el cursor no cambiará.

+0

'protected void init (Contexto context, Cursor c, boolean autoRequery)' * Este método está en desuso. No use esto, use el constructor normal. Esto se eliminará en el futuro. * – Mussa

0

nueva consulta() ya está en desuso, simplemente poner en práctica el método updateUI sencilla() como esta en la clase de su hijo CursorAdapter y llamarlo después de las actualizaciones de datos:

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

Es fácil.

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

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

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

O utilice CursorLoader ...

Cuestiones relacionadas