调用setValue()时设置ChoiceBox的显示值

时间:2017-07-02 23:30:35

标签: java javafx-8

我想在ChoiceBox中存储对象以使用(id,name)字段镜像查找表,以便在选择显示文本时稍后保存记录时能够轻松检索id。例如,如果我有一个带有Category对象的ChoiceBox,当用户在下拉列表中选择一个类别时,文本将显示该类别的名称 - 显示和选择将匹配。最后,当模型保存数据时,它会将Category对象的id字段作为SQL查询中的参数拉出。

我在这篇文章底部的代码显示了我试图了解正在发生的事情。

挑战是,当值更改时,选择的文本部分不一定更新,无论是使用显式setValue()调用还是将我将绑定绑定到ObjectProperty时的间接调用。

我相信我理解为什么。如果我使用与Choice的ObservableList中相同的Category对象调用setValue()(同一个对象,而不是具有相同字段值的另一个实例),则文本部分显示正确的值。但是,如果我使用另一个实例调用setValue(),即使字段值相同,它也不起作用。我对I类使用的equals()进行了覆盖,但这并没有帮助。是否有另一种方法ChoiceBox用于比较对象?

您有什么建议可以解决这个问题吗?我希望每次实现这个用例时都避免编写其他监听器或搜索函数。

也许一种简单的方法是使用数据库中的id来获取用于构建组合的选择列表中的一个对象,并将其设置为我的模型属性的值。例如,如果id为1,则我的模型的类别字段将设置为= picklist [1](Category对象)。由于选择的ObservableList将使用相同的选项列表进行初始化,因此对象将是相同的并且问题将得到解决。 (与我的代码中的第三个场景一样。)

/*
 * 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);
    }

}

1 个答案:

答案 0 :(得分:0)

问题只是您没有覆盖equals(...)方法,该方法有签名

public boolean equals(Object other) 

用不同的签名定义

public boolean equals(TestClass other)

重载了它。 (当你打算覆盖一个方法时,总是使用@Override注释是一个好习惯。如果你实际上没有覆盖方法,编译器会产生错误,所以你可以立即看到问题。)检查传递给setValue(...)的值是否在支持列表中的机制(可能是通过调用List.contains(...))将使用equals(...)中定义的Object方法没有超越它。

如果您实际覆盖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); 
}

然后这将按照您的意愿运作。

另请注意,覆盖hashCode()时应始终覆盖equals(...)

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