【问题标题】:JavaFX Combobox displays other entry than selectedJavaFX Combobox 显示未选中的其他条目
【发布时间】:2019-01-06 01:14:41
【问题描述】:

我注意到从列表中选择一个值后显示在 ComboBox 中的值可能与所选值不同,如果这两个值根据它们的 equals 方法相等(但根据 toString 方法具有不同的表示,因此显示不同)。

这可以从下面的示例程序中看出

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Group group = new Group();
            Scene scene = new Scene(group,100,40);

            ObservableList<EqualContent> content = FXCollections.observableArrayList();
            content.add(new EqualContent("A"));
            content.add(new EqualContent("B"));

            Label selection = new Label();

            ComboBox<EqualContent> demoBox = new ComboBox<EqualContent>(content);
            demoBox.setOnAction(event -> selection.setText(" selected: "+demoBox.getValue()));

            group.getChildren().add(new HBox(demoBox, selection));

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    class EqualContent {
        private String name;
        EqualContent(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return name;
        }
        @Override
        public boolean equals(Object other) {
            return other != null;
        }
        @Override
        public int hashCode() {
            return 0;
        }
    }
}

选择 B 项会导致以下结果:

此外,之后似乎不可能实际选择 A。

解决此问题的第一个选项显然是修改 equal-method,但我不想在我的情况下这样做,因为我的真实班级也有 compareTo,我希望它们保持一致。

第二个选项是围绕 EqualContent 构建一个包装器类,该类也考虑等值的字符串表示。我当然可以这样做,但对它不太满意。

我是否缺少更简单或更优雅的解决方案?

【问题讨论】:

  • 你的问题是你被覆盖的相等方法。您基本上是说任何不为空的对象都等于所有其他不为空的对象。我猜这意味着ComboBox 的值永远不会改变。如果你删除那个 Overridden equals 方法,你的问题就会消失。
  • fx 中几乎所有的更改通知都基于相等性(相对于身份)- 为任何属性(例如 selectedItem 或组合的值)执行通知的 expressionHelper 仅在 !oldValue.equals 时触发(新值)。没有办法,afaik,除非你的用户界面不基于身份......
  • 您可能会考虑发布您的解决方案作为答案并接受它 - 这样做会让其他人更容易找到(作为解决方案:)
  • 只是一个评论:我永远不会以任何方式使用 toString(调试/日志记录除外)。相反,为包装器建模该独特标准并为 ui 使用 StringConverter。
  • 感谢您的所有反馈。我把我的解决方案变成了一个答案,明天可以接受。至于 toString() 的使用:不是一个“真正的”程序员,我可能对更多的坏习惯感到内疚。当正在进行的工作接近我满意的状态时,我会将其添加到我将更改的内容列表中。 ;)

标签: javafx combobox wrapper equals


【解决方案1】:

从 cmets 看来,我无法使用包装器。我的示例解决方案是以下通用类(根据需要提供更多功能,但我已经在另一个地方有类似的东西):

static class RenameWrapper<T> {
        public final T wrappedObject;
        public final Callback<T, String> renamer;
        RenameWrapper(T wrappedObject, Callback<T, String> renamer) {
            this.wrappedObject = wrappedObject;
            this.renamer = renamer;
        }
        @Override
        public String toString() {
            return renamer.call(wrappedObject);
        }
        public static <S> ArrayList<RenameWrapper<S>> wrapList(List<S> objectsToWrap, Callback<S, String> renamer) {
            ArrayList<RenameWrapper<S>> result = new ArrayList<RenameWrapper<S>>();
            objectsToWrap.forEach(o -> result.add(new RenameWrapper<S>(o, renamer)));
            return result;
        }
        /**
         * This and other are considered equal if other is a RenameWrapper that contains the same
         * wrappedObject according to their equals and the renamer produces the same String representation.
         */
        @Override
        public boolean equals(Object other) {
            if(this == other) return true;
            if(!(other instanceof RenameWrapper)) return false;
            RenameWrapper<?> otherWrapper = (RenameWrapper<?>) other;
            return wrappedObject.equals(otherWrapper.wrappedObject) &&
                    this.toString().equals(other.toString());
        }
        @Override
        public int hashCode() {
            return Objects.hash(wrappedObject, this.toString());
        }
    }

那你就可以了

ObservableList<RenameWrapper<EqualContent>> wrappedContent = FXCollections.observableArrayList(
                    RenameWrapper.wrapList(content, eq -> eq.toString()));

并从 WrappedContent 填充 ComboBox,而不是像以前一样填充内容。

请注意,在这里使用 toString() 并不是很好的做法...

【讨论】:

    猜你喜欢
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-11
    • 2015-08-21
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多