How to Add Background Colour to Group Layout in Javafx

How to add background colour to Group layout in JavaFX?

Solution

Replace your usage of Group with a Pane and things will behave similarly, except you will gain the ability to do stuff like style the pane's background using CSS.

Background

If you want to style a parent node with CSS, use something which derives from Region:

Region is the base class for all JavaFX Node-based UI Controls, and all layout containers. It is a resizable Parent node which can be styled from CSS. It can have multiple backgrounds and borders. It is designed to support as much of the CSS3 specification for backgrounds and borders as is relevant to JavaFX.

A Group is designed to be a very light-weight parent, which incurs minimum processing and storage overhead, hence it supports only very minimal CSS properties (and does not support CSS backgrounds).

Regions, on the other hand, offer extensive CSS styling capabilities.

A Pane is a concrete Region subclass which behaves most like a group (e.g. it does not do implicit layout and you manually lay out the nodes in the pane).

Alternate Solution

This alternate solution allows you to add a "background" node to a group. It works in code, not CSS.

Items which you add to a group are layered by the painting algorithm, from back to front. So add a colored rectangle as the first item in the group and the rectangle will effectively form the background for the group.

Using JavaFx, How do I change a panels background color only inside the padding?

Firstly, there is no css property "-fx-background-padding" in JavaFX CSS.

You can set multiple backgrounds and position them using the css property "-fx-background-insets". This is a very powerful/useful property that almost the entire predefined css (in modena.css) relies on this for styling the controls.

Having said that, there will not be single concrete solution when it comes to styling. You can get the same effect in multiple ways (as specified by @n247s & @jewelsea).

Apart from those two, another way to get the desired behavior is using "-fx-background-insets".

Approach 1

If you are very specific in using "-fx-border-insets", you can include an extra color to the background and control the two colors using -fx-background-padding.

In the below code, I included "transparent" color and managed it to render for required space and then started rendering the desired color(#f4f4f4) from there.

controlBox1.setStyle(
"-fx-background-color: transparent, #f4f4f4;" +
"-fx-background-insets:0, 5;" +
"-fx-background-radius: 5;" +
//"-fx-background-padding: 10;" +
"-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: lightgrey;"
);

Approach 2

I dont recommend using "-fx-border-insets" if you have one border color. If you managed to set margins (as mentioned by @jewelsea) you can just remove "-fx-border-insets".

HBox.setMargin(controlBox2, new Insets(5));
controlBox2.setStyle(
"-fx-background-color: #f4f4f4;" +
"-fx-background-radius: 5;" +
//"-fx-background-padding: 10;" +
"-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
// "-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: lightgrey;"
);

Notice that I included "-fx-background-radius" in both approaches to avoid edged background corners.

Working Example:

Sample Image

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class BackgroundPadding_Demo extends Application {
@Override
public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
root.setStyle("-fx-background-color:lightblue;");
Scene scene = new Scene(root, 500, 200);
stage.setScene(scene);
stage.show();

StackPane controlBox1 = new StackPane(new Label("Box1"));
controlBox1.setAlignment(Pos.TOP_LEFT);
controlBox1.setMinSize(200, 100);
controlBox1.setMaxSize(200, 100);
controlBox1.setStyle(
"-fx-background-color: transparent, #f4f4f4;" +
"-fx-background-insets:0, 5;" +
"-fx-background-radius: 5;" +
//"-fx-background-padding: 10;" +
"-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: lightgrey;"
);

StackPane controlBox2 = new StackPane(new Label("Box2"));
controlBox2.setAlignment(Pos.TOP_LEFT);
controlBox2.setMinSize(200, 100);
controlBox2.setMaxSize(200, 100);
HBox.setMargin(controlBox2, new Insets(5));
controlBox2.setStyle(
"-fx-background-color: #f4f4f4;" +
"-fx-background-radius: 5;" +
//"-fx-background-padding: 10;" +
"-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
// "-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: lightgrey;"
);

HBox hBox = new HBox(controlBox1, controlBox2);
hBox.setSpacing(15);
hBox.setAlignment(Pos.CENTER);
root.getChildren().add(new Group(hBox));
}

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

}

code set background color for Anchorpane in FXML document

Here is an example:



<?import javafx.scene.layout.AnchorPane?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: blue;" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" />

If you are using Scene Builder, you should learn how to use this section under Properties:

Sample Image

You can also use a CSS stylesheet to accomplish this.

JavaFX setting a background via CSS

A Group does not provide the ability to set a background through CSS. You need to use something which extends from Region to do that. Probably you can use a Pane which does more or like the same thing and can be styled with a background through CSS.

If you want to see whats possible for every component the CSS reference guide is the best way.

Java FX CSS Reference Guide

JavaFX: setting background color for Text controls

Based on this solution, this is a quick implementation of a method to provide background coloring for all the Text nodes within a FlowPane, using CSS and the ability to set a series of paint values separated by commas (as much as Text items) and insets for each one of them:

private FlowPane flow;
private Scene scene;

@Override
public void start(Stage primaryStage) {
Text text0 = new Text("These are several ");
Text text1 = new Text("Text Nodes ");
Text text2 = new Text("wrapped in ");
Text text3 = new Text("a FlowPane");
text0.setFill(Color.WHEAT);
text0.setFont(new Font("Times New Roman", 20));
text1.setFill(Color.WHITE);
text1.setFont(new Font("Verdana", 32));
text2.setFill(Color.WHITESMOKE);
text2.setFont(new Font("Arial", 24));
text3.setFill(Color.WHITESMOKE);
text3.setFont(new Font("Arial", 18));

flow = new FlowPane(text0, text1, text2, text3);
scene = new Scene(flow, 300, 200);

primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();

setBackgroundColors();
flow.needsLayoutProperty().addListener((obs,d,d1)->setBackgroundColors());
}

private void setBackgroundColors(){
final Bounds out = flow.getBoundsInLocal();
final StringBuilder sbColors = new StringBuilder();
final StringBuilder sbInsets = new StringBuilder();
AtomicInteger cont = new AtomicInteger();
flow.getChildrenUnmodifiable().forEach(n->{
sbColors.append("hsb(")
.append((((double)cont.get())/((double)flow.getChildren().size()))*360d)
.append(", 60%, 90%)");
Bounds b = ((Text)n).getBoundsInParent();
sbInsets.append(b.getMinY()).append(" ");
sbInsets.append(Math.min(scene.getWidth(),out.getMaxX())-b.getMaxX()).append(" ");
sbInsets.append(Math.min(scene.getHeight(),out.getMaxY())-b.getMaxY()).append(" ");
sbInsets.append(b.getMinX());
if(cont.getAndIncrement()<flow.getChildren().size()-1){
sbColors.append(", ");
sbInsets.append(", ");
}
});
flow.setStyle("-fx-background-color: "+sbColors.toString()+"; -fx-background-insets: "+sbInsets.toString()+";");
}

This will lead to this:

Flow1

and after resizing the scene:

Flow2

EDIT

Based on the OP request of using a TextFlow layout instead of a FlowPane, since Text nodes can be spanned over several lines within a TextFlow, the given solution will no longer be valid, as the bounding box of each text node will overlap others.

As a workaround, we can split the Text nodes in single word Text nodes, while keeping the same background color for those in the same original phrase.

I won't go into the splitting logic, but I will add a list of indices, where each index maps the text node with its index of background color.

private FlowPane flow;
private Scene scene;

private final List<Integer> indices=Arrays.asList(0,0,0,1,1,2,2,3,3);

@Override
public void start(Stage primaryStage) {
List<Text> text0 = Arrays.asList(new Text("These "), new Text("are "), new Text("several "));
List<Text> text1 = Arrays.asList(new Text("Text "), new Text("Nodes "));
List<Text> text2 = Arrays.asList(new Text("wrapped "), new Text("in "));
List<Text> text3 = Arrays.asList(new Text("a "), new Text("FlowPane"));
text0.forEach(t->t.setFill(Color.WHEAT));
text0.forEach(t->t.setFont(new Font("Times New Roman", 20)));
text1.forEach(t->t.setFill(Color.WHITE));
text1.forEach(t->t.setFont(new Font("Verdana", 32)));
text2.forEach(t->t.setFill(Color.WHITESMOKE));
text2.forEach(t->t.setFont(new Font("Arial", 24)));
text3.forEach(t->t.setFill(Color.WHITESMOKE));
text3.forEach(t->t.setFont(new Font("Arial", 18)));

flow = new FlowPane();
flow.getChildren().addAll(text0);
flow.getChildren().addAll(text1);
flow.getChildren().addAll(text2);
flow.getChildren().addAll(text3);
scene = new Scene(flow, 300, 200);

primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();

setBackgroundColors();
flow.needsLayoutProperty().addListener((obs,d,d1)->setBackgroundColors());
}

private void setBackgroundColors(){
final Bounds out = flow.getBoundsInLocal();
final StringBuilder sbColors = new StringBuilder();
final StringBuilder sbInsets = new StringBuilder();
AtomicInteger cont = new AtomicInteger();
flow.getChildrenUnmodifiable().forEach(n->{
sbColors.append("hsb(")
.append((double)indices.get(cont.get())/(double)(indices.get(flow.getChildren().size()-1)+1)*360d)
.append(", 60%, 90%)");
Bounds b = ((Text)n).getBoundsInParent();
sbInsets.append(b.getMinY()).append(" ");
sbInsets.append(Math.min(scene.getWidth(),out.getMaxX())-b.getMaxX()-1).append(" ");
sbInsets.append(Math.min(scene.getHeight(),out.getMaxY())-b.getMaxY()).append(" ");
sbInsets.append(b.getMinX());
if(cont.getAndIncrement()<flow.getChildren().size()-1){
sbColors.append(", ");
sbInsets.append(", ");
}
});
flow.setStyle("-fx-background-color: "+sbColors.toString()+"; -fx-background-insets: "+sbInsets.toString()+";");
}

This FlowPane now behaves as a TextFlow:

Flow3

JavaFX Change ScrollPane ViewPort background color

You have to modify the .viewport color of the ScrollPane with css:

Sample Image

CSS code:

/*The ViewPort of the ScrollPane */
.scroll-pane .viewport {
-fx-background-color: white; /*or whatever you want*/
}

.scroll-pane {
-fx-background-color:transparent;
}

And change these lines of code from your application:

 //........

//.........
root.setPadding(new Insets(15));
root.setTop(menuBar);
//root.setCenter(hbox);

Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setTitle("Dummy Title");
stage.setScene(scene);
stage.show();

//........

//.........

Finally mention that the css file which contains the css code,in this example has to be in the same source folder as the Application.java file.

Sample Image



Related Topics



Leave a reply



Submit