samedi 6 avril 2013

Les ListView et le recyclage



Qu'est ce que le recyclage?

Sur certaines plateformes (Blackberry Java par exemple), chaque cellule était rajoutée dans l'écran. C'est simple et performant... tout du moins tant que l'on implémente pas l'Infinite Scroll qui pourrait nous mener à une consommation mémoire bien trop importante.
(D'expérience si les cellules étaient simples on pouvait tout de même avoir des centaines voire des milliers de cellules sans aucun soucis sur un Curve 8520)

Les ListView des plateformes modernes (ios, Android, BB10) utilisent le concept de recyclage. Dès que la cellule est éloignée de la portion visible de la liste, elle est supprimée de l'écran. Mais on la conserve en mémoire pour pouvoir la réutiliser sans réinstancier tout les objects la composant.
Lorsque l'utilisateur scrolle dans la liste, les cellules qui vont apparaitre sont en fait les cellules précédemment enlevées, replacées dans l'écran et avec des valeurs mises à jour à partir des données de la nouvelle cellule.

SDK BB10: Le ListItemManager gère la création de cellule et leur recyclage


Eviter les problèmes de rafraichissement

Lancer Appworld sur votre BB10 avec une appli comportant de nombreux commentaires. Scrollez dans les commentaires et remonter en haut. Vous ne voyez pas un truc étrange? Là où il y avait 4 étoiles il y en maintenant 5 (par exemple).

C'est typiquement une erreur provenant d'une liste mal recyclée. Pour comprendre d'où elle vient, il faut comprendre comment l'updateItem (cf shéma ci-dessus) fonctionne. Prenez ce code:

ListView { dataModel: XmlDataModel { source: "model.xml" } listItemComponents: [ ListItemComponent { type: "contact" Container { id: itemRoot ImageView { onCreationCompleted: { if(ListItemData.value == 0){ image = "asset:///images/starOff.png" } else{ image = "asset:///images/starOn.png" } } } Label { text: ListItemData.name } } } ] }

L'essence de l'erreur vient du traitement réalisé pendant le onCreationCompleted: cette méthode n'est appelée que à la création de la cellule. Le texte, lui est bien mis à jour: il faut dont toujours privilégier le binding.


Solution

[Modification du post précédent, il existe une solution plus propre et simple]

Très bien, nous utilisons donc juste le mauvais slot. Il suffit alors de récupérer le signal datachanged dans le code de la cellule, et modifier l'image ici:
Container {
id: itemRoot ImageView { id: image}
Label { text: ListItemData.name } 
ListItem.onDataChanged: { if(ListItemData.value == 0){ image = "asset:///images/starOff.png" } else{ image = "asset:///images/starOn.png" } } }




1 commentaire: