【问题标题】:JavaFX TabPane tabs don't update positionJavaFX TabPane 选项卡不更新位置
【发布时间】:2017-12-03 07:25:01
【问题描述】:

我注意到在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),则它会按预期工作。有关如何解决此问题的任何线索?

【问题讨论】:

  • 我认为你发现了一个错误。如果您在 .add(2, yourTab) 中使用 2,您将看到新的 Tab 的位置比它应该在的位置少一。
  • 您能解释一下这是如何导致这种行为的吗?我看不到所选选项卡如何影响它们的位置,因为无论哪种方式都应该删除第一个选项卡并更新列表,导致选项卡 2 显示(并选择)在前面,然后选项卡 1 放在它后面。此外,如果宽度无关紧要,它如何改变行为?编辑:是的错误...
  • 这一定是一个错误。如果在再次添加后打印选项卡的实际位置,它将显示与实际视觉位置不同的位置。现在,如果您在关键事件结束时调用 tabPane.autosize();,它将修复位置。
  • 似乎在 fx9 中工作
  • @kleopatra TabPaneSkin 是否改变了它的异步行为?

标签: java javafx tabs javafx-2 tabpanel


【解决方案1】:

这确实是一个错误,我在公共 JIRA 中找不到它报告它现在报告在 https://bugs.openjdk.java.net/browse/JDK-8193495

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

总结

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

详情

删除标签调用removeTabs,概述如下:

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

窗格处于无效状态的时间是从调用返回到requestLayout 返回(在另一个线程上)的时间。这个持续时间等于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();
});

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

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

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

可能的修复

tabHeaderArea 上的一些同步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-02
    • 2011-10-17
    • 2020-03-03
    • 2017-01-28
    • 2016-09-14
    • 2018-04-03
    • 1970-01-01
    相关资源
    最近更新 更多