JavaFX 11 不可编辑的 ComboBox 无法正确显示组合项列表之外的值

标签 java javafx combobox javafx-11

我在使用 JaxaFX 11 ComboBox 时遇到了问题(看来在 JavaFX 8 中它工作正常)。

对于不可编辑组合,即在按钮单元格中显示所选值(不在可编辑文本框中),如果新值不是,则不会显示任何值(按钮单元格可能被视为“空”)包含在组合的项目列表中,只有一个异常(exception):

如果先前的值为null(例如,通过键盘在弹出列表中取消选择先前的非空值),则新的非空值将正确显示。

查看一个简单的代码来重现该问题。最初,组合值为 null。按按钮设置项目列表之外的值。显示OK。然后从弹出窗口中选择一些值。再次尝试按下按钮。现在,尽管组合值已更改,但组合仍为空。

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ComboTest extends Application {
    private ComboBox<String> testCombo;

    @Override public void start(Stage primaryStage) {
        Button btn = new Button("Set test value outside list");
        btn.setOnAction(e -> {
            testCombo.setValue("test value outside list");
        });

        testCombo = new ComboBox<>(FXCollections.observableArrayList(
                "Option 1", "Option 2", "Option 3"
        ));
        testCombo.setPromptText("null now!");

        TextField valueTextField = new TextField();
        testCombo.valueProperty().addListener((ob, ov, nv) -> {
            valueTextField.setText("combo value: " + nv);
        });

        VBox root = new VBox(5);
        root.setPadding(new Insets(5));
        root.getChildren().addAll(btn, testCombo, valueTextField);

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

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

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

我错过了什么吗?我无法找到任何解决方法。我尝试调试,但找不到答案。似乎首先设置了正确的文本,但随后又将其删除。

(JDK 11.0.2、JavaFX 11.0.2、Netbeans 10)

最佳答案

对我来说看起来像一个错误:由于某种原因,在选择包含的值时,在设置不包含的值时,displayNode(即buttonCell的内容)不会更新。仅通过 ComboBoxBaseSkin 上的公共(public) api 访问 displayNode 即可触发正确的设置。

要查看它的更新,请将以下行添加到您的示例中,然后在不显示未包含的值后单击该按钮:

Button display = new Button("getDisplayNode");
display.setOnAction(e -> {
    ((ComboBoxBaseSkin) testCombo.getSkin()).getDisplayNode();
});

为了解决这个问题,我们可以扩展组合的皮肤并在每个布局过程中强制更新:

public static class MyComboBoxSkin<T> extends ComboBoxListViewSkin<T> {

    public MyComboBoxSkin(ComboBox<T> control) {
        super(control);
    }

    @Override
    protected void layoutChildren(double x, double y, double w, double h) {
        super.layoutChildren(x, y, w, h);
        // must be wrapped inside a runlater, either before or after calling super
        Platform.runLater(this::getDisplayNode);
    }

}

用法:

testCombo = new ComboBox<>(FXCollections.observableArrayList("Option 1", "Option 2", "Option 3")) {
    @Override
    protected Skin<?> createDefaultSkin() {
        return new MyComboBoxSkin<>(this);
    }
};

注意:皮肤实现大量使用了多个 boolean 脏标志,这些标志在这种特殊情况下似乎会产生破坏性的交互(不幸的是,不明白到底是如何交互的)。使用 Platform.runlater 延迟访问似乎有效。


更新

经过进一步挖掘,它看起来像是 regressionlazy-dirty介绍(不是我的措辞,尽管喜欢它:)修复。 custom cell implementation Tom 提供的效果很好。

关于JavaFX 11 不可编辑的 ComboBox 无法正确显示组合项列表之外的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55378762/

相关文章:

JavaFX - 刷新标签

javascript - 更新后 jQuery 自动完成组合框不起作用

vba - Excel 表单组合框值到单元格

java - Android/Java - 消除顺序加载动画延迟

java - Large Switch Case(60 个不同的 Cases)——我该如何减少?或完善代码

java - 将cpp函数指针转换为java接口(interface)方法

extjs - 如何使用组合框编辑器获取网格中的当前行

java - java中如何删除字符串中第一次出现字符后的内容?

java - 如何完全禁用 TextArea 对象的默认 Cursor.TEXT

java - JavaFX 节点的父类可以通过访问/确定吗?