43 votes

Android SimpleCursorAdapter ne se met pas à jour lorsque la base de données change

J'ai un Android ListActivity qui s'appuie sur une base de données Cursor par le biais d'un SimpleCursorAdapter .

Lorsque l'on clique sur les éléments, un champ indicateur dans la ligne correspondante de la base de données est basculé et la vue dans la liste doit être mise à jour.

Le problème est que, lorsque la vue qui a été mise à jour sort de l'écran et est recyclée, l'ancienne valeur est affichée sur la vue lorsqu'elle revient à l'écran. La même chose se produit lorsque la liste est redessinée (changement d'orientation, etc.).

J'utilise notifydatasetchanged() pour rafraîchir l'adaptateur du curseur mais cela semble inefficace.

Comment dois-je mettre à jour la base de données pour que le curseur soit également mis à jour ?

90voto

CommonsWare Points 402670

Appelez requery() sur le Cursor quand vous changez des données dans la base de données et que vous voulez qu'elles soient reflétées dans cette base de données. Cursor (ou des choses que l Cursor se remplit, comme un ListView via un CursorAdapter ).

A Cursor est semblable à un curseur ODBC côté client - il contient toutes les données représentées par le résultat de la requête. Par conséquent, ce n'est pas parce que vous modifiez les données dans la base de données que le système d'information de la Cursor ne sera pas au courant de ces changements à moins que vous ne l'actualisiez par le biais de la fonction requery() .


UPDATE : Toute cette question et cet ensemble de réponses devraient être supprimés pour cause de vieillesse, mais c'est apparemment impossible. Quiconque cherche des réponses sur Android doit garder à l'esprit que Android est une cible qui évolue rapidement, et que les réponses de 2009 sont généralement moins bonnes que les réponses plus récentes.

La solution actuelle consiste à obtenir un nouveau Cursor et utiliser soit changeCursor() o swapCursor() sur le CursorAdapter pour effectuer un changement de données.

0 votes

Que fait donc notifydatachanged ?

12 votes

Il n'existe pas de "notifydatachanged". Si vous voulez dire notifyDataSetChanged() sur l'adaptateur, c'est ainsi que le SimpleCursorAdapter indique à la ListView que les données ont été modifiées. Pour citer la documentation, "Notifie la vue jointe que les données sous-jacentes ont été modifiées et qu'elle doit se rafraîchir." Cependant, votre problème n'est pas que l'adaptateur informe le ListView de la modification - votre problème est que l'adaptateur ne sait pas que les données ont changé. L'appel à requery() est le moyen de résoudre ce problème avec un CursorAdapter.

0 votes

Cela a du sens maintenant. J'ai mal compris le flux de données.

38voto

rony l Points 1774

requery est maintenant déprécié. de la documentation :

Cette méthode est obsolète. Ne l'utilisez pas. Demandez simplement un nouveau curseur, afin de pouvoir effectuer cette opération de manière asynchrone et mettre à jour votre vue de liste une fois que le nouveau curseur est revenu.

après avoir obtenu un nouveau curseur, on peut utiliser la fonction adapter.changeCursor(cursor) . cela devrait mettre à jour la vue.

0 votes

Merci pour l'info, j'ai eu un crash sur Honeycomb lié à cursor.requery(), peut-être que cela est derrière cela.

0 votes

La situation courante est de changer la valeur de onClickListener(). Ce qui m'intrigue est que d'une part, nous envoyons le curseur initial par le constructeur (c'est-à-dire que la requête est écrite en dehors de l'adaptateur) mais d'autre part, le onlickLister est défini comme une classe imbriquée dans l'adaptateur. Donc la requête pour le nouveau curseur sera écrite à l'intérieur de la classe de l'adaptateur. Cela peut entraîner une duplication du code. Quelle est la bonne conception pour écrire la requête une seule fois dans le code source ?

21voto

siefca Points 325

En cas d'utilisation du chargeur et du curseur généré automatiquement, vous pouvez appeler :

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

dans votre activité, juste après avoir modifié quelque chose sur une BD, pour régénérer le nouveau curseur. N'oubliez pas de définir également des gestionnaires d'événements :

@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 votes

J'ai essayé une demi-douzaine de choses différentes sur Stack Overflow et c'est celle qui a finalement fonctionné pour moi. Merci !

1 votes

C'est exactement la réponse que je cherchais, en utilisant un simple adaptateur, et en appelant swapCursor onLoadFinished.

1 votes

Ça a marché, mais j'ai dû ajouter ceci : Comme le fragment listview enregistre un clic utilisateur provoquant une mise à jour des valeurs d'une ligne qui doit ensuite être insérée dans la base de données, le nouveau curseur créé automatiquement par le nouveau cursorloader dans onCreateLoader(), a dû être transmis à l'adaptateur (dans mon cas) personnalisé étendant simpleCursorAdapter, parce que je n'ai trouvé aucun autre moyen de notifier la méthode getView() (effectuant la mise à jour de la vue) au sujet du nouveau curseur. GetView() utilisait l'ancien curseur jusqu'à ce que le fragment entier soit redémarré. J'ai donc créé un setCursor() dans le customadapter et l'ai appelé depuis onCreateLoader(). Et ça a marché.

1voto

gjican Points 11

Je ne sais pas si vous avez défini le autoRequery de CursorAdapter à true .

L'adaptateur vérifiera le autoRequery propriété ; si elle est false le curseur ne sera pas modifié.

0 votes

protected void init (Context context, Cursor c, boolean autoRequery) Cette méthode est obsolète. Ne l'utilisez pas, utilisez le constructeur normal. Elle sera supprimée à l'avenir.

0voto

Jaroslav Points 181

Requery() est déjà déprécié, implémentez simplement la méthode simple updateUI() comme ceci dans la classe enfant de votre CursorAdapter et appelez-la après la mise à jour des données :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X