java - 如何修复 javafx 中的渲染错误(ComboBox、ListView)

标签 java javafx combobox rendering

在一个相当复杂的 javafx 应用程序中,我遇到了一个可能的渲染错误。最近两天我可以追踪到以下简单的应用程序。以下 SSCCE 演示了在某些情况下某些 javafx 组件未正确呈现。结果,ComboBox 和 ListView 不显示更改的内容:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ComboManagedBug extends Application {
  public static void main(String[] args) {
    launch(args);
  }

  public void start(Stage primaryStage) {
    // add combo box
    ComboBox<String> combo = new ComboBox<>();
    combo.setPromptText("Choose a value...");
    combo.getItems().setAll("1", "2", "3");

    // add list view
    ListView<Label> list = new ListView<>();

    // add "add" button
    Button add = new Button("Add");
    add.setOnAction(e -> list.getItems().add(
      new Label(combo.getSelectionModel().getSelectedItem())));

    // add tab pane
    Tab tab1 = new Tab("First", new VBox(combo, add, list));
    Tab tab2 = new Tab("Second");
    TabPane tabs = new TabPane(tab1, tab2);
    tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); // important!

    // add "next" and "cancel" buttons at bottom
    Button next = new Button("Next");
    Button cancel = new Button("Cancel (Triggers refresh)");
    HBox buttons = new HBox(next, cancel);

    // install tab listener
    tabs.getSelectionModel().selectedItemProperty().addListener((a, b, c) -> {
      // intention is to show next button only on first tab
      boolean firstTab = c == tab1;
      next.setVisible(firstTab);
      next.setManaged(firstTab); // important!
    });

    // show
    VBox root = new VBox(new VBox(tabs, buttons)); // important!
    Scene scene = new Scene(root, 400, 400);
    primaryStage.setTitle("ComboBox/ListView Rendering Bug Demo");
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

重现步骤:

  1. 启动程序
  2. 选择组合值 -> ComboBox 显示选择的值 -> 确定
  3. 单击“添加”-> 将值添加到 ListView 并显示 -> 确定
  4. 激活第二个标签
  5. 激活第一个标签
  6. 选择一个与前一个不同的组合值 -> ComboBox 中的值不会改变 -> BUG
  7. 点击“添加”->ListView内容没有变化->BUG
  8. 单击底部的“取消”-> 虽然没有向此按钮添加监听器,但 ComboBox 和 ListView 现在都显示正确的值。因此,这两个容器似乎都包含正确的值,但在触发某些 UI 刷新之前它们不会正确呈现。

请注意,重现此错误需要满足三个重要条件(标有“重要”):

  1. 切换选项卡时切换“下一步”按钮的管理状态以隐藏此按钮(隐藏节点以及节点可见状态的常用方法,请参阅 JavaFX - setVisible doesn't "hide" the element)
  2. TabClos​​ingPolicy.UNAVAILABLE(很常见的情况)
  3. VBox 中的 VBox(嵌套不同的 javafx 组件节点时,在现实生活中很容易发生这种情况)。

这个错误是已知的吗?有人知道解决这个问题的方法吗?我尝试了 Platform.runLater(cancel.fire()) 和类似的东西,但没有成功。

谢谢你的任何提示,彼得。

顺便说一句,除此之外,我们公司使用 javafx 已经有几年了。根据我们的经验,它非常可靠并且编程 javafx 很有趣。我希望我们的问题有一个简单的解决方案:)

最佳答案

正如原帖评论中所讨论的,该错误并未出现在 JavaFX 8 (Oracle) 中,而是出现在更高版本 (OpenJFX) 中。我找到了以下解决方法:

public static void fixTabRendering(TabPane tabs) {
    if (tabs.getTabClosingPolicy() != TabClosingPolicy.UNAVAILABLE) return;
    tabs.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB);
    for (Node node : tabs.lookupAll(".tab-close-button")) {
        // hide "close" button to imitate TabClosingPolicy.UNAVAILABLE
        node.setStyle("-fx-background-color:transparent;-fx-shape:null;-fx-pref-width:0.001");
    }
}

此代码应在显示舞台后运行(否则 lookupAll() 返回 null)以及在将选项卡添加到选项卡 Pane 后运行。后者可以通过选项卡监听器实现:

tabs.getTabs().addListener((Change<?> change) ->
    Platform.runLater(() -> fixTabRendering(tabs)));

Platform.runLater() 是必需的,否则 lookupAll() 可能不会返回添加的选项卡的节点。

也许这个解决方案可以帮助某人:)

关于java - 如何修复 javafx 中的渲染错误(ComboBox、ListView),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58044487/

相关文章:

java - 在Java中将不同类型的数据序列化为ORC格式

java - spring mvc从xml文件读取配置

JavaFX 2 媒体兼容性?

C# 如何在组合框文本中添加值?

c# - 从组合框中获取选定的对象

java - JavaFX Platform.runLater 的使用和从不同线程访问 UI

java - 使用 Gson get() 从 Json 获取值

java - 在 TableView ScrollBar 上添加监听器

java - Maven:JavaFX:将库捆绑在一个 Jar 文件中

javascript - 使用存储在 "dynamic"数组中的数据构建菜单或组合框