在一个相当复杂的 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();
}
}
重现步骤:
- 启动程序
- 选择组合值 -> ComboBox 显示选择的值 -> 确定
- 单击“添加”-> 将值添加到 ListView 并显示 -> 确定
- 激活第二个标签
- 激活第一个标签
- 选择一个与前一个不同的组合值 -> ComboBox 中的值不会改变 -> BUG
- 点击“添加”->ListView内容没有变化->BUG
- 单击底部的“取消”-> 虽然没有向此按钮添加监听器,但 ComboBox 和 ListView 现在都显示正确的值。因此,这两个容器似乎都包含正确的值,但在触发某些 UI 刷新之前它们不会正确呈现。
请注意,重现此错误需要满足三个重要条件(标有“重要”):
- 切换选项卡时切换“下一步”按钮的管理状态以隐藏此按钮(隐藏节点以及节点可见状态的常用方法,请参阅 JavaFX - setVisible doesn't "hide" the element)
- TabClosingPolicy.UNAVAILABLE(很常见的情况)
- 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/