To customize the appearance of tabs, you have the option to use a stylesheet with a custom color lookup or format a stylesheet using a data URL and apply a new version whenever a style change is required.
The CSS rules for styling tabs can be found in the modena.css
. Additional documentation on CSS structure can be found in the JAVA CSS reference.
While the example in this solution utilizes an inline stylesheet defined in a data URL for the looked-up color, the stylesheet for the looked-up color solution (TAB_LABEL_CSS
) could also be defined externally.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatedTabApp extends Application {
private static final String[] textColors = {
"#FF0000", "#FF3300", "#FF6600", "#FF9900",
"#FFCC00", "#FFFF00", "#CCFF00", "#99FF00",
"#66FF00", "#33FF00", "#00FF00", "#00FF33",
"#00FF66", "#00FF99", "#00FFCC", "#00FFFF",
"#00CCFF", "#0099FF", "#0066FF", "#0033FF",
"#0000FF", "#3300FF", "#6600FF", "#9900FF",
"#CC00FF", "#FF00FF"
};
private static final String TAB_LABEL_CSS =
"""
data:text/css,
.root {
-my-tab-label-color: red;
}
.tab-pane > .tab-header-area > .headers-region > .tab > .tab-container > .tab-label {
-fx-text-fill: -my-tab-label-color;
}
.tab-pane > .tab-header-area > .headers-region > .tab {
-fx-base: #3c3f4a;
}
""";
private static final String TAB_LABEL_CSS_FORMATTABLE =
"""
data:text/css,
.tab-pane > .tab-header-area > .headers-region > .tab > .tab-container > .tab-label {
-fx-text-fill: %s;
}
.tab-pane > .tab-header-area > .headers-region > .tab {
-fx-base: #3c3f4a;
}
""";
public void start(Stage stage) {
TabPane tabPane = new TabPane(
createTab("Apple", Color.PALEGREEN),
createTab("Orange", Color.PAPAYAWHIP),
createTab("Banana", Color.LIGHTYELLOW)
);
tabPane.setTabClosingPolicy(
TabPane.TabClosingPolicy.UNAVAILABLE
);
Timeline timeline = new Timeline();
timeline.setAutoReverse(true);
timeline.setCycleCount(Timeline.INDEFINITE);
for (int i = 0; i < textColors.length; i++) {
final int finalI = i;
KeyFrame keyFrame = new KeyFrame(
Duration.seconds(i * .08),
// color change mechanism using a looked up color
e -> tabPane.setStyle(
"-my-tab-label-color: " + textColors[finalI] + ";"
)
// alternate color change mechanism using a formattable style sheet
// e -> tabPane.getStylesheets().setAll(
// TAB_LABEL_CSS_FORMATTABLE.formatted(
// textColors[finalI]
// )
// )
);
timeline.getKeyFrames().add(keyFrame);
}
Scene scene = new Scene(tabPane);
scene.getStylesheets().add(TAB_LABEL_CSS);
stage.setScene(scene);
stage.show();
timeline.play();
}
private static Tab createTab(String name, Color color) {
return new Tab(
name,
new Rectangle(
200, 100,
color
)
);
}
public static void main(String[] args) {
launch(args);
}
}
FAQ
Why not use lookup() to get the "tab-label"?
lookup()
relies on the current structure of the scene graph, which can be unstable due to changes like adding or removing tabs.
The initialization of the scene graph can also be deferred in some cases, making it advisable to use stylesheets over lookup methods.
Wouldn't it be easier to set the Graphic of the Tab to a Label and style the Label directly?
Using a graphic
for an animated label on a tab is also a valid method. The choice between the stylesheet-based approach and using a graphic depends on personal preference and experience.
If desired, a separate answer detailing the graphic approach could be added in the future.