【问题标题】:JavaFX change selected tab close button background programmaticallyJavaFX以编程方式更改选定的选项卡关闭按钮背景
【发布时间】:2020-02-22 13:21:44
【问题描述】:

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

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 TabPaneSkin$TabHeaderSkin$2@7d620380[styleClass=tab-close-button] //现在我切换到 tab2
javafx.scene.control.Tab@416ca04a TabPaneSkin$TabHeaderSkin$2@7d620380[styleClass=tab-close-button]

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

感谢您的帮助!

【问题讨论】:

    标签: javafx tabs javafx-css


    【解决方案1】:

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

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

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

    • 使用“查找颜色”。这些就像 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 实现的细节(即标签关闭按钮的默认颜色名称)。

    以编程方式打开或关闭功能的更好方法是使用自定义 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);
        }
    
    }
    

    【讨论】:

    • 我假设您正确阅读了这个问题。措辞似乎比我的更符合你的解释。
    • 感谢您的回答,切换标签时效果很好,但不幸的是,正如我所说,我不能使用外部 CSS,因为我必须检查侦听器内部的一些条件,然后决定是否颜色会改变。这就是为什么我认为以编程方式解决方案会更好。不过还是谢谢。
    • @adxl 您可以使用我在帖子末尾展示的技术来决定是否使用自定义颜色;要完全关闭它,只需将查找的颜色设置为默认值:'-fx-mark-color'
    • @adxl 我更新了答案,提供了几种“程序化”方法来挂钩外部 CSS 文件,并添加了一些解释,说明为什么我认为您的方法不起作用。
    • @James_D 我尝试实现第二种技术(查找颜色)并且效果很好,所以我想我说外部 CSS 没有帮助是错误的。也感谢您的清晰解释和努力!
    猜你喜欢
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 2011-06-12
    • 1970-01-01
    • 2011-03-07
    • 2020-05-15
    • 1970-01-01
    相关资源
    最近更新 更多