BB10: ListView et problèmes de recyclage

Qu’est ce que le recyclage?

Pour afficher une liste d’objets, on peut tout simplement tous les ajouter à l’écran, simple et efficace. Sur BlackBerry Java on pouvait créer des listes de milliers d’éléments sans soucis de performance. Mais quand les listes deviennent complexes ou infinies, la mémoire vive peut vite saturer.

Il faut alors ne charger que les cellules visibles à l’écran, plus les prochaines à venir. Lorsque l’on scrolle vers le bas, la liste va créer à la volée de nouvelles cellules, tandis que les cellules du dessus sont supprimées.
Mais pourquoi ne pas tout simplement récupérer les cellules du dessus pour les remettre en bas avec les nouvelles données? Ce concept est appelé le recyclage et est fortement utilisé dans beaucoup de frameworks.

SDK BB10: ListItemManager et le recyclage des Listview

Reproduire le problème

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 qu’à la création de la cellule. Le Label, quant à lui est bien mis à jour: il faut dont toujours privilégier le binding.

Solution

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Container {
id: itemRoot
ImageView { id: myImage}
Label { text: ListItemData.name }

ListItem.onDataChanged: {
if(ListItemData.value == 0){
myImage.image = "asset:///images/starOff.png"
}
else{
myImage.image = "asset:///images/starOn.png"
}
}
}

onDataChanged sera appelé dès que la donnée est mise à jour, soit au début et à chaque recyclage.