JavaFX TabPane 排序选项卡造成严重破坏
JavaFX TabPane sorting tabs creates havoc
如果您在 space 不足以显示所有选项卡时尝试对选项卡进行排序,JavaFX TabPane 会出现非常奇怪的行为。
更准确地说,选项卡选择按钮(选项卡窗格右侧带有向下箭头的圆形按钮 header)应显示包含所有选项卡的 drop-down 列表,没有显示任何东西。
我创建了一个小测试来重现该问题。只需在 "Add new tab & sort" 上单击几次(直到没有足够的 space 用于所有选项卡)(或在 "Add new tab" 上单击几次,然后单击 "Sort tabs"),然后单击右上角的选项卡选择按钮...只是看到它根本没有显示!
请注意,调整 window 的大小以使所有选项卡适合,然后重新调整大小使选项卡选择按钮再次出现,问题就解决了。
这是要重现的代码。我正在使用 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();
}
}
Tab
列表的 TabPaneSkin
中有一个 ListChangeListener
,但正如您已经提到的那样,它无法对列表进行排序。
作为解决方法,您可以将选项卡放在新列表中并在排序后将其应用于 TabPane
:
List<Tab> tabs = new ArrayList(tabPane.getTabs());
tabs.sort((o1, o2) -> o2.getText().compareTo(o1.getText()));
tabPane.getTabs().clear();
tabPane.getTabs().setAll(tabs);
我想我在 TabPaneSkin
class' 方法 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
这是一个问题,因为:
- 相反的方法
addTabs
不做相反的事情(即不将项目添加到弹出菜单中,并且
tabHeaderArea.controlButtons.popup
通过订阅 tabPane.getTabs()
:
中的更改自行管理其条目
tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> setupPopupMenu());
所以他们都从弹出菜单中删除了项目,但是因为 setupPopupMenu
在 removeTabs
之前被调用,当使用 [=18 重新添加选项卡时,条目不会重新添加回来=].
我从 removeTabs
方法中删除了以上几行,它工作得很好。
将向 JDK 提交错误...
更新:
在 http://Bugs.java.com 提交了错误报告(评论 ID JI-9038050),但修复它的希望很小(我最后一次提交错误报告是在 2015 年 9 月,仍然是 "pending")。
与此同时,丑陋的解决方法(感谢@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);
<...>
如果您在 space 不足以显示所有选项卡时尝试对选项卡进行排序,JavaFX TabPane 会出现非常奇怪的行为。
更准确地说,选项卡选择按钮(选项卡窗格右侧带有向下箭头的圆形按钮 header)应显示包含所有选项卡的 drop-down 列表,没有显示任何东西。
我创建了一个小测试来重现该问题。只需在 "Add new tab & sort" 上单击几次(直到没有足够的 space 用于所有选项卡)(或在 "Add new tab" 上单击几次,然后单击 "Sort tabs"),然后单击右上角的选项卡选择按钮...只是看到它根本没有显示!
请注意,调整 window 的大小以使所有选项卡适合,然后重新调整大小使选项卡选择按钮再次出现,问题就解决了。
这是要重现的代码。我正在使用 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();
}
}
Tab
列表的 TabPaneSkin
中有一个 ListChangeListener
,但正如您已经提到的那样,它无法对列表进行排序。
作为解决方法,您可以将选项卡放在新列表中并在排序后将其应用于 TabPane
:
List<Tab> tabs = new ArrayList(tabPane.getTabs());
tabs.sort((o1, o2) -> o2.getText().compareTo(o1.getText()));
tabPane.getTabs().clear();
tabPane.getTabs().setAll(tabs);
我想我在 TabPaneSkin
class' 方法 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
这是一个问题,因为:
- 相反的方法
addTabs
不做相反的事情(即不将项目添加到弹出菜单中,并且
中的更改自行管理其条目tabHeaderArea.controlButtons.popup
通过订阅tabPane.getTabs()
:tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> setupPopupMenu());
所以他们都从弹出菜单中删除了项目,但是因为 setupPopupMenu
在 removeTabs
之前被调用,当使用 [=18 重新添加选项卡时,条目不会重新添加回来=].
我从 removeTabs
方法中删除了以上几行,它工作得很好。
将向 JDK 提交错误...
更新:
在 http://Bugs.java.com 提交了错误报告(评论 ID JI-9038050),但修复它的希望很小(我最后一次提交错误报告是在 2015 年 9 月,仍然是 "pending")。
与此同时,丑陋的解决方法(感谢@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);
<...>