JavaFX Combobox 显示除所选之外的其他条目
JavaFX Combobox displays other entry than selected
我注意到,在从列表中 selecting 一个后,ComboBox 中显示的值可能不同于 selected 的一个,如果这两个值根据它们的等值方法相等(但根据 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;
}
}
}
其中 selecting 项目 B 导致以下内容:
此外,似乎不可能 select A 之后。
解决这个问题的第一个选择显然是修改 equal-method,但我不想在我的情况下这样做,因为我的真实 class 也有一个 compareTo,我想要它们保持一致。
第二个选项是围绕 EqualContent 构建一个包装器 class,该包装器还考虑了 equals 的字符串表示形式。我当然可以做到,但我对此不太满意。
我是否缺少更简单或更优雅的解决方案?
从评论看来我无法绕过使用包装器。我的示例解决方案是以下通用 class(根据需要提供更多功能,但我已经在另一个地方有类似的东西):
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 中填充组合框,而不是像以前那样填充内容。
请注意,在这里使用 toString() 并不是很好的做法...
我注意到,在从列表中 selecting 一个后,ComboBox 中显示的值可能不同于 selected 的一个,如果这两个值根据它们的等值方法相等(但根据 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;
}
}
}
其中 selecting 项目 B 导致以下内容:
此外,似乎不可能 select A 之后。
解决这个问题的第一个选择显然是修改 equal-method,但我不想在我的情况下这样做,因为我的真实 class 也有一个 compareTo,我想要它们保持一致。
第二个选项是围绕 EqualContent 构建一个包装器 class,该包装器还考虑了 equals 的字符串表示形式。我当然可以做到,但我对此不太满意。
我是否缺少更简单或更优雅的解决方案?
从评论看来我无法绕过使用包装器。我的示例解决方案是以下通用 class(根据需要提供更多功能,但我已经在另一个地方有类似的东西):
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 中填充组合框,而不是像以前那样填充内容。
请注意,在这里使用 toString() 并不是很好的做法...