JavaFX 以编程方式更改选定的选项卡关闭按钮背景

JavaFX change selected tab close button background programmatically

我试图仅更改 selected 选项卡的关闭图标背景,问题是当有一个选项卡时它可以工作,但是当我添加更多时没有任何反应,直到我关闭第一个选项卡第一,这是我所做的:

tabPane.lookup(".tab:selected:top").lookup(".tab-container").lookup(".tab-close-button").setStyle("-fx-background-color: red");

现在的问题是,当我 select 另一个选项卡并尝试更改它的颜色时,没有任何反应,直到我关闭之前更改过的选项卡。

快速示例:
我有 2 个带有白色关闭按钮的选项卡,
我 select tab1 : 颜色变了,
我 select tab2 : 没有任何反应,
我关闭 tab1 然后 select tab2 : tab2 的关闭按钮改变了。

selected 选项卡是正确的,因为如果我为每个选项卡打印这两行:

System.out.println(tabPane.getSelectionModel().getSelectedItem());
System.out.println(tabPane.lookup(".tab:selected:top").lookup(".tab-container").lookup(".tab-close-button"));

这是结果:

javafx.scene.control.Tab@6d81182e <----tab1
TabPaneSkin$TabHeaderSkin@7d620380[styleClass=tab-close-button] <----tab1
//now I switch to tab2
javafx.scene.control.Tab@416ca04a <----tab2
TabPaneSkin$TabHeaderSkin@7d620380[styleClass=tab-close-button] <----this address should not be the same as the first one.

PS : 我在 CSS 中做不到,因为颜色应该动态变化,当我切换选项卡时,该行在侦听器中执行。

感谢您的帮助!

CSS 查找本质上依赖于渲染框架的实现细节,因此它们可能不可靠,您应该尽可能避免使用它们。特别是,如果未在场景图上进行 CSS 传递(通常在渲染期间发生),或者如果自上次 [=51] 状态发生变化,则查找将不起作用=] 通过。我认为您的代码发生的情况是,在 CSS 状态更新为新的选择状态之前调用侦听器,导致您从 .tab:selected 查找中检索到错误的 Tab (虽然我决不能确定这就是正在发生的事情)。

请注意,您已经拥有所需的大部分动态行为,因为 "selected" 伪类将动态更新。

实现此类功能的最佳方法是定义一个涵盖所有可能样式的外部 CSS 文件,然后使用多种机制之一以编程方式修改 CSS 状态。有几种方法可以做到这一点:

  • 使用"looked-up colors"。这些就像 CSS.
  • 中的变量名
  • 使用自定义伪类,您可以将其用作 CSS 文件中的选择器,并且您可以通过编程更新其状态。

这是使用第一种方法的示例。在这里,我们在名为 "selected-tab-color" 的 CSS 文件中定义了一种查找颜色,它最初将关闭按钮设置为蓝色。

tabpane.css:

.tab-pane {
    selected-tab-color: blue ;
}

.tab:selected > .tab-container > .tab-close-button {
    -fx-background-color: selected-tab-color ;
}

在 Java 代码中,我们可以简单地通过调用 tabPane.setStyle("selected-tab-color: red;") 来更新它,你可以这样做,例如,在事件处理程序中(但实际上在任何地方,只要它在JavaFX 应用线程)。在这个例子中,我们只是列出了选定的选项卡,并使一个选项卡有一个红色的关闭按钮,其他选项卡为蓝色。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TabPaneTest extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        TabPane tabPane = new TabPane();
        Tab tab1 = new Tab("Tab 1", new Label("Tab 1"));
        Tab tab2 = new Tab("Tab 2", new Label("This tab has\na custom close button"));
        Tab tab3 = new Tab("Tab 3", new Label("Tab 3"));
        tabPane.getTabs().addAll(tab1, tab2, tab3);

        tabPane.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) -> {
            if (newTab == tab2) { 
                tabPane.setStyle("selected-tab-color: red;");
            } else {
                tabPane.setStyle("selected-tab-color: blue;");
            }

        });

        BorderPane root = new BorderPane(tabPane);

        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("tabpane.css").toExternalForm());


        primaryStage.setScene(scene);
        primaryStage.setWidth(250);
        primaryStage.setHeight(250);
        primaryStage.show();
    }

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

}

您可以使用此技术完全关闭效果,方法是恢复为默认设置:

tabPane.setStyle("selected-tab-color: -fx-mark-color;");

然而,这依赖于了解默认 CSS 实现的细节(即选项卡关闭按钮的默认颜色的名称)。

以编程方式打开或关闭功能的一种稍微好一点的方法是使用自定义伪类。它们像任何其他 CSS 伪类一样工作,并且可以通过编程方式修改它们的状态。

在此示例中,仅当包含选项卡窗格具有 warn-on-close 伪类集时,关闭按钮的颜色才为红色:

.tab-pane:warn-on-close .tab:selected > .tab-container > .tab-close-button {
    -fx-background-color: red ;
}

并且在此处的 Java 代码中,我们在选择选项卡 2 时打开它,在选择其他选项卡时关闭它。同样,这里可以使用任意逻辑。

import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TabPaneTest extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        TabPane tabPane = new TabPane();
        Tab tab1 = new Tab("Tab 1", new Label("Tab 1"));
        Tab tab2 = new Tab("Tab 2", new Label("This tab has\na custom close button"));
        Tab tab3 = new Tab("Tab 3", new Label("Tab 3"));
        tabPane.getTabs().addAll(tab1, tab2, tab3);

        PseudoClass warnOnClosePseudoClass = PseudoClass.getPseudoClass("warn-on-close");
        tabPane.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) -> 
            tabPane.pseudoClassStateChanged(warnOnClosePseudoClass, newTab == tab2));

        BorderPane root = new BorderPane(tabPane);

        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("tabpane.css").toExternalForm());


        primaryStage.setScene(scene);
        primaryStage.setWidth(250);
        primaryStage.setHeight(250);
        primaryStage.show();
    }

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

}