Nik Novák Nik Novák - 3 months ago 52
Java Question

JavaFX - how to get background color of Tab, Button, etc

Problem description: I can't get background of object in JavaFX. I don't mean Shapes, but normal Nodes like Buttons, Tabs and others. I don't know how to access to theirs background color.

What I want? I am developing IDE and I want to run Color animation on tab with file that user want to open and is already existing in program file collection. Before doing this animation I want to read original tab background color and that color is returned to tab at the end of animation. Also I want to get back

hover
and
selected
properties, which disappear when I set some color in animation and they never get back. All colors I am setting up in CSS file and I don't want to change it.

My question: How to get and set programmatically Node color? Or how to do color animation with save original properties and at the end of animation get this properties back?

One short example:

One short example

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<TabPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="480.0" prefWidth="600.0" stylesheets="@style.css" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<tabs>
<Tab text="Sample tab 1">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
<Tab text="Sample tab 2">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
<Tab text="Sample tab 3">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>


styles.css

.tab{
-fx-background-color: pink;}

.tab:hover{
-fx-background-color: red;}

.tab:selected{
-fx-background-color: yellow;}

Answer

As far as I know, there is no way in the public API to determine what is being currently used as the background color for a Region (including for a Control) (unless you know it is either set by an inline style, in which case you can parse the result of getStyle() or by a call to setBackground(...)). But I see no reason you would want this; the color will revert to that defined in the css file if you remove any inline styles or background property.

Here's a simple example where the background color is set by a linear gradient (via an inline style) which slides as a task progresses:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ColoredTabDemo extends Application {

    private int tabCount ;

    @Override
    public void start(Stage primaryStage) {
        TabPane tabPane = new TabPane();
        for (int i = 0; i < 4; i++) {
            tabPane.getTabs().add(createTab());
        }
        Scene scene = new Scene(tabPane, 600, 400);
        scene.getStylesheets().add("colored-tab-demo.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Tab createTab() {
        Tab tab = new Tab("Tab "+(++tabCount));
        Button button = new Button("Load file...");

        button.setOnAction(e -> {
            Task<Void> task = new Task<Void>() {
                @Override
                public Void call() throws Exception {

                    // simulate loading:
                    for (int i=1; i <= 500; i++) {
                        updateProgress(i, 500);
                        Thread.sleep(20);
                    }

                    return null ;

                }
            };

            IntegerBinding progressAsPercent = Bindings.createIntegerBinding(() -> 
                (int) (task.getProgress() * 100), task.progressProperty());

            tab.styleProperty().bind(Bindings.format("-fx-background-color: "
                    + "linear-gradient(to right, -fx-accent 0%%, -fx-accent %d%%, -fx-background %1$d%%, -fx-background 100%%);", 
                    progressAsPercent));

            button.setDisable(true);

            task.setOnSucceeded(evt -> {
                tab.styleProperty().unbind();
                tab.setStyle("");
                button.setDisable(false);
            });

            new Thread(task).start();
        });

        tab.setContent(new StackPane(button));

        return tab ;
    }

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

colored-tab-demo.css is almost exactly the same as you posted, but using a looked-up color instead of setting -fx-background-color directly:

.tab{
    -fx-background-color:   -fx-background;
    -fx-background: pink ;
}

.tab:hover{
    -fx-background:   red;
}

.tab:selected{
    -fx-background:   yellow;
}