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.

entrez la description de l'image ici

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

Author: Community, 2014-12-24

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)..

 3
Author: eckig, 2014-12-24 08:57:43