JavaFX 11无法编辑的ComboBox无法在组合项目列表外部正确显示值

时间:2019-03-27 13:45:55

标签: java javafx combobox javafx-11

我在 JaxaFX 11 ComboBox 上遇到了麻烦(似乎在JavaFX 8中可以正常使用)。

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

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

查看简单的代码来重现该问题。最初,组合值为null。按下按钮在项目列表之外设置一个值。显示确定。然后从弹出窗口中选择一些值。再按一次该按钮。现在,即使组合值已更改,组合也保持为空。

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)

2 个答案:

答案 0 :(得分:2)

对我来说似乎是个错误:由于某种原因,在选择包含值时,在设置未包含值时,displayNode(即buttonCell的内容)不会更新。仅通过ComboBoxBaseSkin上的公共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);
    }
};

注意:皮肤实现大量使用了多个布尔脏标志,在这种特殊情况下,它们似乎具有破坏性的交互作用(不幸的是,我不了解它的精确程度)。使用Platform.runlater延迟访问似乎有效。


更新

经过进一步的挖掘,它看起来像是regression(不是我的措辞,虽然喜欢它)引入的lazy-dirty修复程序。汤姆(Tom)提供的custom cell implementation工作正常。

答案 1 :(得分:1)

回答我自己的问题。我发现了另一个可能的解决方法。尽管我不确定它是否“安全”,但它似乎可以正常工作(按钮单元永远不需要为空吗?)。

重点是:使用自定义按钮单元格,覆盖updateItem(T item, boolean empty),并且(不同于标准单元格实现),不做任何操作(返回) {{ 1}},即不要擦除单元格-否empty = true

背后是什么?看来问题不在于按钮单元文本的设置不正确,而是稍后被某些“空单元逻辑”删除了...

您可以将此代码添加到我的示例中:

setText(null)