我有以下程序:
public class MainClass extends Application {
public static void main(String[] arg) {
launch(arg);
}
@Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane();
tabPane.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> {
System.out.println("Tab change: " + oldTab + "/" + newTab);
});
Tab tab = new Tab("Test tab");
tab.setOnCloseRequest((event) -> {
System.out.println("Removing tab");
event.consume();
//I need to remove tab manually
tabPane.getTabs().remove(tab);
});
System.out.println("Adding tab");
tabPane.getTabs().add(tab);
Group root = new Group(tabPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
当我运行它并单击选项卡上的关闭图标时,我有以下程序输出:
Adding tab
Tab change: null/javafx.scene.control.Tab@70b1aa69
Removing tab
Tab change: javafx.scene.control.Tab@70b1aa69/null
Tab change: null/javafx.scene.control.Tab@70b1aa69
如你所见,当我关闭标签时,我会收到两个Tab change
个事件,但我只需要一个。如何解决?
答案 0 :(得分:2)
有趣的错误 - 令人困惑的是,即使不再在选项卡列表中,删除的选项卡仍然可以作为选定选项卡的原因/方式。
第一个问题是,确切选择的位置:在TabHeaderSkin安装的mousePressedHandler中完成
setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent me) {
if (getTab().isDisable()) {
return;
}
if (me.getButton().equals(MouseButton.MIDDLE)) {
if (showCloseButton()) {
Tab tab = getTab();
if (behavior.canCloseTab(tab)) {
removeListeners(tab);
behavior.closeTab(tab);
}
}
} else if (me.getButton().equals(MouseButton.PRIMARY)) {
behavior.selectTab(getTab());
}
}
});
但是接下来:在删除标签后,这个处理程序仍然处于活动状态(希望它的视觉效果也是如此)?视觉部分的清理是TabPaneSkin的任务,它监听选项卡列表并删除TabHeaderSkin(也就是:显示其内容上方的选项卡的组件)。但由于两个原因,清理工作并未立即完成:
TabHeaderSkin的代码:
private void removeListeners(Tab tab) {
listener.dispose();
inner.getChildren().clear();
getChildren().clear();
// following line is missing:
setOnMousePressed(null)
}
一种黑客攻击的方法是在选项卡上注册我们自己的侦听器,并在删除时强制处理程序为null。注意:必须在核心完成其工作后通知侦听器,因此要么安装在自定义皮肤中,要么在设置tabPane的皮肤之后。
为了说明,我相应地修改了你的例子:
public class TabPaneRemoveSelected extends Application {
public static void main(String[] arg) {
launch(arg);
}
public static class MyTabSkin extends TabPaneSkin {
public MyTabSkin(TabPane pane) {
super(pane);
pane.getTabs().addListener(this::tabsChanged);
}
protected void tabsChanged(Change<? extends Tab> c) {
while (c.next()) {
if (c.wasRemoved()) {
// lookup all TabHeaderSkins
Set<Node> tabHeaders = getSkinnable().lookupAll(".tab");
tabHeaders.stream()
.filter(p -> p instanceof Parent)
.map(p -> (Parent) p)
.forEach(p -> {
// all children removed indicates being in the process
// of being removed
if (p.getChildrenUnmodifiable().size() == 0) {
// complete removeListeners
p.setOnMousePressed(null);
}
}
);
}
}
}
}
@Override
public void start(Stage primaryStage) throws Exception {
TabPane tabPane = new TabPane() {
@Override
protected Skin<?> createDefaultSkin() {
return new MyTabSkin(this);
}
};
tabPane.getSelectionModel().selectedItemProperty().addListener((ov, oldTab, newTab) -> {
System.out.println("Tab change: " + oldTab + "/" + newTab );
});
Tab tab = new Tab("Test tab");
Tab second = new Tab("second");
installHandler(tabPane, tab, second);
installHandler(tabPane, second);
System.out.println("Adding tab");
tabPane.getTabs().addAll(tab, second);
Group root = new Group(tabPane);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
protected void installHandler(TabPane tabPane, Tab... tab) {
for (Tab tab2 : tab) {
tab2.setOnCloseRequest((event) -> {
System.out.println("Removing tab");
event.consume();
//I need to remove tab manually
tabPane.getTabs().remove(tab2);
});
}
}
}
答案 1 :(得分:1)
这似乎是一个错误所以我打开了一个错误问题http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8189424(https://bugs.openjdk.java.net/browse/JDK-8189424)并接受了这个答案(只要SO允许我这样做)。