JavaFX TabPane排序选项卡会造成严重破坏

时间:2016-05-19 16:12:01

标签: java sorting javafx tabpanel

如果在没有足够空间显示所有选项卡时尝试对选项卡进行排序,则JavaFX TabPane会有一种非常奇怪的行为。

更确切地说,选项卡选择按钮(选项卡窗格标题右侧带有向下箭头的圆形按钮)应显示包含所有选项卡的下拉列表,不显示任何内容

我创建了一个小测试来重现问题。只需单击几次(直到所有选项卡都没有足够的空间)“添加新选项卡&排序”(或“添加新选项卡”,然后“排序选项卡”几次),然后单击选项卡选项右上角的按钮......只是为了看它根本没有显示任何内容!

enter image description here

请注意,调整窗口大小以使所有选项卡都适合,然后将其调整大小以便再次显示选项卡选择按钮,从而解决问题。

这是重现的代码。我正在使用 jdk1.8.0_92 。看起来像JDK错误?

public class TabPaneTest extends Application {

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

int i = 1;

@Override
public void start(Stage primaryStage) throws Exception {
    TabPane tabPane = new TabPane();

    tabPane.getTabs().add(new Tab("My beautiful tab " + i, new TextArea("pane " + (i++))));

    Button add = new Button("Add new tab");
    add.setOnAction(event -> {
        tabPane.getTabs().add(new Tab("My beautiful tab " + i, new TextArea("pane " + (i++))));
    });

    Button addSort = new Button("Add new tab & sort");
    addSort.setOnAction(event -> {
        tabPane.getTabs().add(new Tab("My beautiful tab " + i, new TextArea("pane " + (i++))));
        tabPane.getTabs().sort((o1, o2) -> o2.getText().compareTo(o1.getText()));
    });
    Button sort = new Button("Sort tabs");
    sort.setOnAction(event -> {
        tabPane.getTabs().sort((o1, o2) -> o2.getText().compareTo(o1.getText()));
    });


    VBox vbox = new VBox(tabPane, new HBox(add, addSort, sort));

    primaryStage.setScene(new Scene(vbox));
    primaryStage.setWidth(400);
    primaryStage.setHeight(300);
    primaryStage.show();
}
}

2 个答案:

答案 0 :(得分:3)

ListChangeListener列表TabPaneSkin中有一个Tab,但不知道如上所述,它不会对列表进行排序。

作为一种解决方法,您可以将标签放在新列表中,并在排序后将其应用于TabPane

 List<Tab> tabs = new ArrayList(tabPane.getTabs());
 tabs.sort((o1, o2) -> o2.getText().compareTo(o1.getText()));
 tabPane.getTabs().clear();
 tabPane.getTabs().setAll(tabs);

答案 1 :(得分:3)

我想我在TabPaneSkin课程中发现了问题&#39;方法removeTabs:它会从tabHeaderArea.controlButtons.popup

中删除条目
                // remove the menu item from the popup menu
            ContextMenu popupMenu = tabHeaderArea.controlButtons.popup;
            TabMenuItem tabItem = null;
            if (popupMenu != null) {
                for (MenuItem item : popupMenu.getItems()) {
                    tabItem = (TabMenuItem) item;
                    if (tab == tabItem.getTab()) {
                        break;
                    }
                    tabItem = null;
                }
            }
            if (tabItem != null) {
                tabItem.dispose();
                popupMenu.getItems().remove(tabItem);
            }
            // end of removing menu item

这是一个问题,因为:

  1. 相反的方法addTabs没有相反的方法(即,不会将项目添加到弹出菜单中,并且
  2. tabHeaderArea.controlButtons.popup通过订阅tabPane.getTabs()中的更改来自行管理其条目:

        tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> setupPopupMenu());
    
  3. 因此,他们都会从弹出菜单中删除这些项目,但由于在setupPopupMenu之前调用removeTabs,因此在使用addTabs重新添加标签时,不会重新添加条目

    我从removeTabs方法删除了上述行,它运行正常。

    将向JDK提交错误...

    更新:

    http://Bugs.java.com(审核ID JI-9038050)提交了一个错误报告,但没有希望它会被修复(我在2015年9月提交的错误报告仍然是#34;等待&#34;)

    与此同时,丑陋的解决方法(感谢@jns)是删除所有选项卡,对它们进行排序,然后再添加:

            List<Tab> tabs = Lists.newArrayList(tabPane.getTabs());
            tabs.sort((o1, o2) -> o2.getText().compareTo(o1.getText()));
            tabPane.getTabs().clear();
            tabPane.getTabs().setAll(tabs);
    

    这很难看,因为你实际上可以看到标签消失然后再回来。

    更新2:

    更好的解决方法(再次感谢@jns)是在插入之前确定新选项卡的正确位置:

        Comparator<Tab> comparator = (o1, o2) -> o2.getText().compareTo(o1.getText());
        Button addSort = new Button("Add new tab, sorted");
        addSort.setOnAction(event -> {
            Tab newTab = new Tab("My beautiful tab " + i, new TextArea("pane " + (i++)));
    
            // THIS IS WRONG! See UPDATE 3 below:
            // int pos = Math.max(0, Collections.binarySearch(tabPane.getTabs(), newTab, comparator));
            tabPane.getTabs().add(pos, newTab);
        });
    

    只有在每次插入新标签时排序顺序不变时,这才有效。如果您需要使用其他排序顺序对现有选项卡进行排序,则仍需要删除所有选项卡,排序,然后重新添加(请参阅解决方法1)。

    更新3:

    事实证明,Java binarySearch只搜索精确匹配,而不是返回下限,因为我(经验丰富的C ++开发人员;)会期望......所以你需要设置以下的暴行来找到插入点:

        <...>
        int pos = 0;
        while(pos < tabPane.getTabs().size() && tabPane.getTabs().get(pos).getText().compareTo(newTab.getText()) < 0) {
            pos++;
        }
        tabPane.getTabs().add(pos, newTab);
        <...>