JavaFX ChoiceBox -如何更新弹出项目的文本?



我有一个ChoiceBox,我可以为我的程序选择语言。当我选择另一种语言时,标签按需要被翻译(因为它是使用ChoiceBoxSkin#getDisplayText重新计算的,我的StringConverter考虑了语言),但弹出列表中的元素保持不变。

现在,我可以这样写

public void updateStrings() {
var converter = getConverter();
setConverter(null);
setConverter(converter);
var selected = valueProperty().getValue();
valueProperty().setValue(null);
valueProperty().setValue(selected);
}

在我的ChoiceBox-子类。这将用正确翻译的文本重新填充弹出列表。再次设置该值是必要的,因为ChoiceBoxSkin#updatePopupItems(在更改转换器时触发)也会重置toggleGroup。这意味着选中的项目将不再在弹出列表中被标记为选中。

尽管有点难看,但这实际上适用于我当前的用例。但是,如果valueProperty的任何侦听器在将其设置为null或第二次选择所需项目时出现问题,则会中断。

我是错过了一个更干净的还是更好的方法来实现这一点?

另一种方法可能是使用自定义ChoiceBoxSkin。扩展它,我可以访问ChoiceBoxSkin#getChoiceBoxPopup(尽管它被注释为"仅用于测试"),并且可以将RadioMenuItems的文本属性绑定到相应的翻译后的StringProperty。但一旦ChoiceBoxSkin#updatePopupItems从其他任何地方被触发,这种情况就会中断…

MRP应该是:

import javafx.scene.control.ChoiceBox;
import javafx.util.StringConverter;
public class LabelChangeChoiceBox extends ChoiceBox<String> {
private boolean duringUpdate = false;
public LabelChangeChoiceBox() {
getItems().addAll("A", "B", "C");
setConverter(new StringConverter<>() {
@Override
public String toString(String item) {
return item + " selected:" + valueProperty().getValue();
}
@Override
public String fromString(String unused) {
throw new UnsupportedOperationException();
}
});
valueProperty().addListener((observable, oldValue, newValue) -> {
if(duringUpdate) {
return;
}
duringUpdate = true;
updateStrings();
duringUpdate = false;
});
}
public void updateStrings() {
var converter = getConverter();
setConverter(null);
setConverter(converter);
var selected = valueProperty().getValue();
valueProperty().setValue(null);
valueProperty().setValue(selected);
}
}

和像

这样的application类
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import ui.LabelChangeChoiceBox;
public class Launcher extends Application {
@Override
public void start(Stage stage) {
Scene scene = new Scene(new LabelChangeChoiceBox());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

这个工作,但需要duringUpdate变量,如果有另一个更改侦听器,可能会中断。

我不确定这是否符合您的需求,因为您对问题的描述有几个地方不清楚。

这是一个使用自己选择的语言更新其转换器的ChoiceBox,并在发生更改时保留其值:

import java.util.Locale;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.layout.BorderPane;
import javafx.util.StringConverter;
public class FXLocaleSelector
extends Application {
@Override
public void start(Stage stage) {
ChoiceBox<Locale> choiceBox = new ChoiceBox<>();
choiceBox.getItems().addAll(
Locale.ENGLISH,
Locale.FRENCH,
Locale.GERMAN,
Locale.ITALIAN,
Locale.CHINESE,
Locale.JAPANESE,
Locale.KOREAN
);
choiceBox.converterProperty().bind(
Bindings.createObjectBinding(
() -> createConverter(choiceBox.getValue()),
choiceBox.valueProperty()));
BorderPane pane = new BorderPane(choiceBox);
pane.setPadding(new Insets(40));
stage.setScene(new Scene(pane));
stage.setTitle("Locale Selector");
stage.show();
}
private StringConverter<Locale> createConverter(Locale locale) {
Locale conversionLocale =
(locale != null ? locale : Locale.getDefault());
return new StringConverter<Locale>() {
@Override
public String toString(Locale value) {
if (value != null) {
return value.getDisplayName(conversionLocale);
} else {
return "";
}
}
@Override
public Locale fromString(String s) {
return null;
}
};
}
public static void main(String[] args) {
launch(FXLocaleSelector.class, args);
}
}

不完全确定我是否正确理解了您的要求,我的假设:

  • 有一个包含语言的选择框你的ui,包括它本身:让我们说它包含项目Locale。英语和语言环境。德语,其项目的视觉表现应该是"英语"、"德语";如果它的值是Locale。英语和"英语"、"德语";如果它的值是Locale。德国
  • 可视化表示是由一个StringConverter完成的,该StringConverter可配置值为

如果是这样,解决方案是分离关注点-实际上,它不是:问题中描述的问题(和攻击!)是JDK-8088507:设置转换器不会更新下拉菜单项的选择。一个hack有好有坏,我个人更喜欢的是一个自定义皮肤

  • 向转换器属性
  • 添加一个更改监听器
  • 反射地调用updateSelection

类似:

public static class MyChoiceBoxSkin<T> extends ChoiceBoxSkin<T> {
public MyChoiceBoxSkin(ChoiceBox<T> control) {
super(control);
registerChangeListener(control.converterProperty(), e -> {
// my local reflection helper, use your own
FXUtils.invokeMethod(ChoiceBoxSkin.class, this, "updateSelection");
});
}
}

注意:黑客-这不是OP的解决方案-不能解决第一次打开时弹出窗口的缺失偏移(最初或在弹出窗口中选择一个项目后)。


不是问题的解决方案,只是一种具有值相关转换器的方法;)

  • 有一个固定值的StringConverter(为了简单)用于转换
  • 有一个带有该值的属性的转换器控制器和一个带有该值配置的转换器的第二个属性:确保转换器在改变值时被替换
  • 将控制器的值绑定到框的值,将框的转换器绑定到控制器的转换器

在(非常原始的)代码:

public static class LanguageConverter<T> extends StringConverter<T> {
private T currentLanguage;
public LanguageConverter(T language) {
currentLanguage = language;
}
@Override
public String toString(T object) {
Object value = currentLanguage;
return "" + object + (value != null ? value : "");
}
@Override
public T fromString(String string) {
return null;
}
}
public static class LanguageController<T> {
private ObjectProperty<StringConverter<T>> currentConverter = new SimpleObjectProperty<>();
private ObjectProperty<T> currentValue = new SimpleObjectProperty<>() {
@Override
protected void invalidated() {
currentConverter.set(new LanguageConverter<>(get()));
}
};
}

用法:

ChoiceBox<String> box = new ChoiceBox<>();
box.getItems().addAll("A", "B", "C");
box.getSelectionModel().selectFirst();
LanguageController<String> controller = new LanguageController<>();
controller.currentValue.bind(box.valueProperty());
box.converterProperty().bind(controller.currentConverter);

最新更新