2017-07-02 3 views
0

Je souhaite stocker des objets dans un ChoiceBox pour mettre en miroir une table de recherche avec des champs (id, name) afin de pouvoir récupérer facilement l'ID lors de la sauvegarde ultérieure de l'enregistrement choix affiche le texte. Par exemple, si j'ai un ChoiceBox avec des objets Catégorie, lorsque l'utilisateur sélectionne une catégorie dans la liste déroulante, le texte affichera le nom de cette catégorie - l'affichage et la sélection correspondront. Éventuellement, lorsque le modèle enregistre les données, il extrait le champ id de l'objet Category en tant que paramètre dans la requête SQL.Définition de la valeur affichée de ChoiceBox lors de l'appel de setValue()

Mon code au bas de cet article montre mes tentatives pour comprendre ce qui se passe. Le défi est que la partie texte du Choix n'est pas nécessairement mise à jour lorsque la valeur change, soit avec un appel explicite setValue(), soit avec un appel indirect lorsque je lie le Choix à une ObjectProperty.

Je crois comprendre pourquoi. Si j'appelle setValue() avec un objet Category identique à un dans ObservableList du choix (le même objet, pas une autre instance avec les mêmes valeurs de champ), la portion de texte affiche la valeur correcte. Cependant, si j'appelle setValue() avec une autre instance, cela ne fonctionne pas, même si les valeurs des champs sont les mêmes. J'ai fait un Override sur l'égalité() de la classe que j'utilise, mais cela n'a pas aidé. Existe-t-il une autre méthode que ChoiceBox utilise pour comparer des objets?

Avez-vous des suggestions pour contourner ce problème? Je veux éviter de coder d'autres écouteurs, ou rechercher des fonctions chaque fois que j'implémente ce cas d'utilisation. Peut-être un moyen simple serait d'utiliser l'id de la base de données pour obtenir l'un des objets dans la liste de sélection utilisée pour construire le combo et définir cela comme la valeur de la propriété de mon modèle. Par exemple, si l'ID est 1, le champ category de mon modèle sera défini sur = picklist [1] (un objet Category). Puisque le champ ObservableList du choix serait initialisé avec la même liste de sélection, les objets seraient identiques et le problème serait résolu. (Comme dans le troisième scénario dans mon code.)

/* 
* If I set the value of a ChoiceBox to exact copy of an object 
* in the ObservableList, the text portion is updated accordingly 
* 
* If is set the value with another instance of the same class 
* with the same values, but not the same exact object, 
* the text portion is not updated. 
* 
*/ 
package javafxapplication1; 

import javafx.application.Application; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.event.ActionEvent; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.ChoiceBox; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class JavaFXApplication1 extends Application { 

    class TestClass { 
     // public to simplify demo - normally private 

     public int id; 
     public String name; 

     public TestClass(int id, String name) { 
      this.id = id; 
      this.name = name; 
     } 

     public boolean equals(TestClass other) { 
      // not called ... 
      System.out.println("TestClass.equals() was called"); 
      return this.id == other.id && this.name.equals(other.name); 
     } 

     @Override 
     public String toString() { 
      return "[id = " + id + " name = " + name + "]"; 
     } 
    } 

    @Override 
    public void start(Stage primaryStage) { 

     ChoiceBox<String> cbS = new ChoiceBox<>(); 
     ObservableList<String> valuesS = FXCollections.observableArrayList(); 
     valuesS.addAll("A", "B", "C"); 
     cbS.setItems(valuesS); 

     // a text converter would make the choice display only text 
     // no the toString() of the class 

     /* 
     * SET INITIAL VALUE BEFORE SHOWING VIEW 
     * no issues with String choice 
     */ 
     cbS.setValue("A"); 
     System.out.println("Initial String value: " + cbS.getSelectionModel().getSelectedItem()); 

     Button btn = new Button(); 
     btn.setText("Change String Value"); 
     btn.setOnAction((ActionEvent event) -> { 
      cbS.setValue("C"); 
      System.out.println("setValue(\"C\") was called"); 
      System.out.println("New String value is " + cbS.getSelectionModel().getSelectedItem()); 
     }); 

     ChoiceBox<TestClass> cbO = new ChoiceBox<>(); 
     ObservableList<TestClass> valuesO = FXCollections.observableArrayList(); 
     TestClass specificInstance = new TestClass(3, "Exact instance"); 
     valuesO.addAll(new TestClass(1, "Aba"), new TestClass(2, "Ubu"), specificInstance); 
     cbO.setItems(valuesO); 
     cbO.setValue(new TestClass(1, "Aba")); 
     System.out.println("Initial Object value: " + cbO.getSelectionModel().getSelectedItem()); 


     Button btnO = new Button(); 
     btnO.setText("Change Object Value"); 
     /* 
     * SET VALUE TO OTHER INSTANCE WITH SAME FIELD VALUES 
     */ 
     btnO.setOnAction((ActionEvent event) -> { 
      cbO.setValue(new TestClass(2, "Ubu")); 
      System.out.println("setValue(same-values-object)"); 
      System.out.println("New Object value is " + cbO.getSelectionModel().getSelectedItem()); 
     }); 

     /* 
     * SET VALUE TO ONE OF THE OBJECTS IN THE CHOICE 
     */ 
     Button btnO2 = new Button(); 
     btnO2.setText("Change Object Value"); 
     btnO2.setOnAction((ActionEvent event) -> { 
      cbO.setValue(specificInstance); 
      System.out.println("setValue(exact-instance-of-object) was called"); 
      System.out.println("New Object value is " + cbO.getSelectionModel().getSelectedItem()); 
     }); 

     VBox root = new VBox(); 
     root.getChildren().addAll(cbS, btn, cbO, btnO, btnO2); 

     Scene scene = new Scene(root, 300, 250); 

     primaryStage.setTitle("Test setValue()"); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     launch(args); 
    } 

} 
+0

Utilisez toujours le '@ annotation Override' lorsque vous substituez les méthodes ... –

Répondre

0

Le problème est simplement que vous ne l'avez pas la méthode substituée equals(...), qui a une signature

public boolean equals(Object other) 

En définissant avec une signature différente

public boolean equals(TestClass other) 

vous avez surchargé il. (Il est recommandé de toujours utiliser l'annotation @Override lorsque vous souhaitez remplacer une méthode.Le compilateur génère une erreur si vous ne remplacez pas la méthode, ce qui vous permet de voir le problème immédiatement.) La machine qui vérifie si la valeur passer à setValue(...) est dans la liste de sauvegarde (probablement via un appel à List.contains(...)) utilisera la méthode equals(...) définie dans Object si vous ne l'avez pas substituée.

Si vous substituez effectivement equals(...):

@Override 
public boolean equals(Object other) { 
    System.out.println("TestClass.equals() was called"); 

    if (this == other) return true ; 

    // cue endless arguments about whether to use instanceof or getClass()... 
    if (! (other instanceof TestClass)) return false ; 

    TestClass tc = (TestClass) other ; 
    return this.id == tc.id && Objects.equals(this.name, tc.name); 
} 

alors ceci fonctionne comme vous le souhaitez.

Notez également que vous devez toujours passer outre hashCode() lorsque vous substituez equals(...):

@Override 
public int hashCode() { 
    return Objects.hash(id, name); 
} 
+0

Excellent. Merci. C'est ce que le problème était. L'exemple ci-dessus fonctionne maintenant, plus le projet sur lequel je travaille. La liaison bidirectionnelle entre SimpleObjectProperty dans mon modèle met à jour le ComboChoice dans la vue lors du chargement de la vue. De plus, j'ai maintenant un meilleur modèle pour remplacer la méthode equals() pour les autres classes où j'en ai besoin. –

+0

Voir les mises à jour, si vous envisagez d'utiliser ceci comme modèle général ... –

+0

Netbeans a suggéré des changements pour le hashCode. Cela pourrait faire, je suppose. –