Bogue de rendu JavaFX ListView lors de l'utilisation de cellules de liste personnalisées
Je crée un surlignant l'enregistreur de communication qui utilise un ListView
pour afficher chaque message dans une seule cellule.
Cela fonctionne fondamentalement, mais quand je fais défiler très vite de haut en bas, des choses étranges se produisent. Comme vous pouvez le voir, je mets en surbrillance la cellule qui est actuellement sélectionnée et la cellule où la souris est au-dessus. Après avoir fait défiler de haut en bas, les cellules en surbrillance ne sont pas celles où se trouve mon point de souris et pour certaines cellules, je ne peux pas les sélectionner en cliquer.
J'ai pu reproduire cela dans un exemple très simple. Supposons que nous ayons un ListView
de chaînes avec des MyListCell
personnalisés qui rendent ces cellules en utilisant un TextFlow
. La méthode updateItem
de MyListCell
ressemble à ceci
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
TextFlow textFlow = new TextFlow(new Text(item));
setGraphic(textFlow);
setText(null);
}
Dans l'initialisation, je crée simplement un nouveau tableau observable de chaînes et je définis la fabrique de cellules en conséquence:
public ObservableList<String > content = FXCollections.observableArrayList();
public ListView<String> listView;
@Override
public void initialize(URL location, ResourceBundle resources) {
listView.setCellFactory(cb -> new MyListCell());
listView.setItems(content);
/* ... /*
Le code d'exemple de travail complet se compose uniquement de certains fichiers (vous devez adapter le package chemin):
Question: Quelqu'un peut-il confirmer ce comportement et me dire ce que je fais mal?
Mon système est Ubuntu 14.04 avec Oracle jdk 1.8.0_25
1 answers
Fondamentalement, dans JavaFX, créer un nouveau Node
et l'ajouter au SceneGraph est une opération coûteuse. C'est pourquoi le ListView
utilise un conteneur de mise en page virtualisé, réutilisant ses cellules lors du changement de fenêtre d'affichage au lieu d'en créer de nouvelles.
Ce que vous devez faire est de réutiliser le TextFlow
dans votre ListCell
ou en d'autres termes: Le stocker en tant qu'attribut membre et "seulement" définir ses enfants dans la méthode updateItem()
.
En ce qui concerne le TextFlow
vs HBox
: Le TextFlow
a un mécanisme de mise en page en raison de l'emballage intégré (word). Donc, si HBox
fait ce dont vous avez besoin, vous devriez envisager de l'utiliser à la place (bien sûr en le mettant en cache dans la ListCell).
Vous pouvez essayer de renforcer cela en essayant de mettre en cache les nœuds Text
. Mais actuellement, je ne peux pas vous offrir une solution simple pour cela (un Node
dans JavaFX ne peut être contenu que dans un seul nœud Parent
, donc un simple WeakHashMap
Cache avec computeIfAbsent
ne fonctionnera pas)..