0

我正在尝试仅更改所选选项卡的关闭图标背景,问题是它在有一个选项卡时有效,但是当我添加更多时,直到我关闭第一个选项卡才会发生任何事情,这就是我所做的:

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

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

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

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

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$2@7d620380[styleClass=tab-close-button] <----tab1
//现在我切换到tab2
javafx.scene.control.Tab @416ca04a <----tab2
TabPaneSkin$TabHeaderSkin$2@7d620380[styleClass=tab-close-button] <----这个地址应该和第一个不一样。

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

谢谢您的帮助!

4

1 回答 1

4

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

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

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

  • 使用“查找颜色”。这些就像 CSS 中的变量名一样。
  • 使用自定义伪类,您可以将其用作 CSS 文件中的选择器,并且您可以通过编程方式更新其状态。

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

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 实现的细节(即选项卡关闭按钮的默认颜色名称)。

以编程方式打开或关闭功能的更好方法是使用自定义 Pseudoclass。它们像任何其他 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);
    }

}
于 2020-02-22T13:46:21.947 回答