JavaFX TabPane 选项卡不更新位置

JavaFX TabPane tabs don't update position

我注意到在 TabPane 中添加和删除选项卡时,它无法匹配基础列表中选项卡顺序的位置。仅当至少一个选项卡因父项的宽度而完全隐藏时才会发生这种情况。下面是一些复制问题的代码:

public class TabPaneTester extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = sizeScene();
        primaryStage.setMinHeight(200);
        primaryStage.setWidth(475);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Scene sizeScene(){
        TabPane tabPane = new TabPane();
        tabPane.setTabMinWidth(200);
        tabPane.getTabs().addAll(newTabs(3));
        Scene scene = new Scene(tabPane);
        scene.setOnKeyPressed(e -> tabPane.getTabs().add(1, tabPane.getTabs().remove(0)));
        return scene;
    }

    private static Tab[] newTabs(int numTabs){
        Tab[] tabs = new Tab[numTabs];
        for(int i = 0; i < numTabs; i++) {
            Label label = new Label("Tab Number " + (i + 1));
            Tab tab = new Tab();
            tab.setGraphic(label);
            tabs[i] = tab;
        }
        return tabs;
    }

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

}

当您按下一个键时,它会删除第一个标签(在索引 0 处)并将其放回索引 1 处,有效地交换前两个标签。但是,当 运行 选项卡实际上并没有在视觉上交换(即使选项卡切换器菜单 确实 切换了它们的位置)。

如果您更改屏幕宽度以包括隐藏的第三个选项卡的像素(将 475 替换为 500),它会按预期工作。关于如何解决这个问题的任何线索?

这确实是一个错误,我在 public JIRA 中找不到它的报告,现在在 https://bugs.openjdk.java.net/browse/JDK-8193495.[=26 中报告了它=]

我所有的分析都是基于TabPaneSkin中的代码,如果你想看的话。

总结

当您删除然后添加选项卡时会出现问题 "too quickly"。删除选项卡后,将在删除过程中进行异步调用。如果您进行其他更改,例如在异步调用完成之前添加一个选项卡(或至少 "finish enough"),则更改过程会看到窗格处于无效状态。

详情

删除选项卡调用 removeTabs,概述如下:

  1. 调用了各种内部清除方法。
  2. 然后它检查关闭是否应该是动画的。
    • 如果是(GROW),
      1. 动画将对 requestLayout 方法的调用排队,该方法本身是异步调用的,
      2. 并且动画开始(异步)并且方法 returns.
    • 如果不是 (NONE),
      1. requestLayout 被立即调用,方法 returns.

窗格处于无效状态的时间是从调用 returns 到 requestLayout returns(在另一个线程上)的时间。这个持续时间等于 requestLayout 的持续时间加上动画的持续时间(如果有的话),即 ANIMATION_SPEED = 150[ms]。在此期间调用 addTabs 可能会导致不良影响,因为正确添加选项卡所需的数据尚未准备好。

解决方法

在调用之间添加人工暂停:

ObservableList<Tab> tabs = tabPane.getTabs();
PauseTransition p = new PauseTransition(Duration.millis(150 + 20));
scene.setOnKeyPressed(e -> {
    Tab remove = tabs.remove(0);
    p.setOnFinished(e2 -> tabs.add(1, remove));
    p.play();
});

这是对 return 的异步调用的足够时间(不要太快地连续调用 KeyPressed 处理程序,因为删除选项卡的速度会比添加选项卡的速度快)。您可以使用

关闭移除动画
tabPane.setStyle("-fx-close-tab-animation: NONE;");

这允许您减少暂停时间。在我的机器上 15 是安全的(这里你也可以连续快速调用 KeyPressed 处理程序,因为延迟很短)。

可能的修复

tabHeaderArea 上的一些同步。