Background with 2 Colors in Javafx

JavaFX multicolor background Rectangle

There are a bunch of different ways to do this. I'd probably prefer using the functionality already defined in a ProgressBar and using CSS to style it to get the colors you want; or just use a regular Pane and use two nested background colors and vary the insets. These and various other options using rectangles etc are shown in the demo below:

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TwoColoredRectangle extends Application {

@Override
public void start(Stage primaryStage) {
// property to change:
DoubleProperty life = new SimpleDoubleProperty(100);

// "animate" the property for demo:
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(life, 100)),
new KeyFrame(Duration.seconds(2), new KeyValue(life, 0))
);

// button to set the animation going:
Button startButton = new Button("Start");
startButton.setOnAction(e -> timeline.playFromStart());

VBox root = new VBox(5);

// Solution 1. One rectangle on another:
Rectangle base = new Rectangle(0, 0, 400, 20);
base.setFill(Color.GRAY);

Rectangle lifeRect = new Rectangle(0, 0, 400, 20);
lifeRect.setFill(Color.GREEN);
lifeRect.widthProperty().bind(life.multiply(4));

Pane solutionPane1 = new Pane(base, lifeRect);
root.getChildren().add(createSolutionPane("Dynamic rectangle on fixed rectangle", solutionPane1));

// Solution 2: Two rectangles changing together:
Rectangle leftRect = new Rectangle();
leftRect.setHeight(20);
leftRect.setFill(Color.GREEN);

Rectangle rightRect = new Rectangle();
rightRect.setHeight(20);
rightRect.setFill(Color.GRAY);

leftRect.widthProperty().bind(life.multiply(4));
rightRect.xProperty().bind(leftRect.widthProperty());
rightRect.widthProperty().bind(life.multiply(-4).add(400));

Pane solutionPane2 = new Pane(leftRect, rightRect);
root.getChildren().add(createSolutionPane("Two dynamic rectangles", solutionPane2));

// Solution 3: Green rectangle on gray-styled pane:
Pane basePane = new Pane();
basePane.setMinSize(400, 20);
basePane.setMaxSize(400, 20);
// gray color will be defined in CSS:
basePane.getStyleClass().add("base-pane");

Rectangle rect = new Rectangle();
rect.setHeight(20);
rect.setFill(Color.GREEN);
rect.widthProperty().bind(life.multiply(4));

basePane.getChildren().add(rect);
root.getChildren().add(createSolutionPane("Dynamic rectangle on pane", basePane));

// Solution 4: Dynamically-styled pane:
Pane dynamicPane = new Pane();
dynamicPane.setMinSize(400, 20);
dynamicPane.setMaxSize(400, 20);
dynamicPane.getStyleClass().add("dynamic-pane");
// make background insets depend on the property:
dynamicPane.styleProperty().bind(life.multiply(4).asString("-fx-background-insets: 0, 0 0 0 %f"));

root.getChildren().add(createSolutionPane("Dynamically styled pane", dynamicPane));

// Solution 5: Progress bar:
ProgressBar progressBar = new ProgressBar();
progressBar.setPrefSize(400, 20);
progressBar.progressProperty().bind(life.divide(100));
root.getChildren().add(createSolutionPane("Progress bar", progressBar));


root.getChildren().add(startButton);

Scene scene = new Scene(root);
scene.getStylesheets().add("two-colored-rectangle.css");

primaryStage.setScene(scene);
primaryStage.show();
}

private Node createSolutionPane(String title, Node content) {
VBox vbox = new VBox(new Label(title), content);
vbox.getStyleClass().add("solution-pane");
return vbox ;
}

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

and the stylesheet (two-colored-rectangle.css):

.root {
-fx-padding: 20 ;
-fx-alignment: center ;
}

.base-pane {
-fx-background-color: gray ;
}

.dynamic-pane {
-fx-background-color: green, gray ;
}

.solution-pane {
-fx-spacing: 5 ;
-fx-padding: 10 ;
}

.progress-bar {
-fx-accent: green ;
-fx-control-inner-background: gray ;
}

JavaFX Slider with 2 different colors , for example green for selected area and red for unselected area

1 slider and 2 progress bars , I will post below the .fxml code , .java code and the needed .css for look and feel :)

Any question feel free to answer :)

As for the code , this is created for XR3Player (Open Source Github Project)

Sample Image

Sample Image

Sample Image

.fxml:

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

<?import java.lang.String?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>


<fx:root prefHeight="389.0" prefWidth="228.0" style="-fx-background-color: #202020;" stylesheets="@../../style/application.css" type="StackPane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/9.0.1">
<children>
<BorderPane fx:id="borderPane" minHeight="0.0" minWidth="0.0">
<bottom>
<StackPane minHeight="0.0" minWidth="0.0" BorderPane.alignment="CENTER">
<children>
<HBox alignment="CENTER" maxHeight="-Infinity" minHeight="0.0" minWidth="0.0" prefHeight="15.0">
<children>
<ProgressBar fx:id="volumeProgress1" maxWidth="1.7976931348623157E308" minHeight="0.0" minWidth="0.0" mouseTransparent="true" prefHeight="15.0" progress="1.0" HBox.hgrow="ALWAYS">
<styleClass>
<String fx:value="transparent-progress-bar" />
<String fx:value="transparent-volume-progress-bar2-nostrip" />
</styleClass>
</ProgressBar>
<ProgressBar fx:id="volumeProgress2" layoutX="10.0" layoutY="10.0" maxWidth="1.7976931348623157E308" minHeight="0.0" minWidth="0.0" mouseTransparent="true" prefHeight="15.0" progress="1.0" HBox.hgrow="ALWAYS">
<styleClass>
<String fx:value="transparent-progress-bar" />
<String fx:value="transparent-volume-progress-bar3-nostrip" />
</styleClass>
</ProgressBar>
</children>
</HBox>
<Slider fx:id="masterVolumeSlider" majorTickUnit="15.0" max="150.0" maxWidth="1.7976931348623157E308" minorTickCount="55" value="75.0">
<styleClass>
<String fx:value="transparency-slider" />
<String fx:value="timer-slider" />
</styleClass>
</Slider>
</children>
<BorderPane.margin>
<Insets left="5.0" right="5.0" />
</BorderPane.margin>
</StackPane>
</bottom>
</BorderPane>
</children>
</fx:root>

.java

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.Slider;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import main.java.com.goxr3plus.xr3player.application.tools.InfoTool;

public class MixTabInterface extends StackPane {

//--------------------------------------------------------------

@FXML
private BorderPane borderPane;

@FXML
private ProgressBar volumeProgress1;

@FXML
private ProgressBar volumeProgress2;

@FXML
private Slider masterVolumeSlider;

// -------------------------------------------------------------

/**
* Constructor.
*/
public MixTabInterface() {

// ------------------------------------FXMLLOADER ----------------------------------------
FXMLLoader loader = new FXMLLoader(getClass().getResource(InfoTool.PLAYERS_FXMLS + "MixTabInterface.fxml"));
loader.setController(this);
loader.setRoot(this);

try {
loader.load();
} catch (IOException ex) {
ex.printStackTrace();
}

}

/**
* Called as soon as fxml is initialized
*/
@FXML
private void initialize() {

//masterVolumeSlider
masterVolumeSlider.boundsInLocalProperty().addListener((observable , oldValue , newValue) -> calculateBars());
masterVolumeSlider.valueProperty().addListener((observable , oldValue , newValue) -> {
calculateBars();
});
}

/**
* Calculate bars positioning
*/
private void calculateBars() {

//Variables
double value = masterVolumeSlider.getValue();
double half = masterVolumeSlider.getMax() / 2;
double masterVolumeSliderWidth = masterVolumeSlider.getWidth();

//Progress Max1
volumeProgress1.setProgress(1);
volumeProgress2.setProgress(1);

//Below is mind tricks
if ((int) value == (int) half) {
volumeProgress1.setMinWidth(masterVolumeSliderWidth / 2);
volumeProgress2.setMinWidth(masterVolumeSliderWidth / 2);
} else if (value < half) {
double progress = 1.0 - ( value / half );
double minimumWidth = masterVolumeSlider.getWidth() / 2 + ( masterVolumeSlider.getWidth() / 2 ) * ( progress );
volumeProgress1.setMinWidth(masterVolumeSliderWidth - minimumWidth);
volumeProgress1.setMaxWidth(masterVolumeSliderWidth - minimumWidth);
volumeProgress2.setMinWidth(minimumWidth);
} else if (value > half) {
double progress = ( value - half ) / half;
double minimumWidth = masterVolumeSlider.getWidth() / 2 + ( masterVolumeSlider.getWidth() / 2 ) * ( progress );
volumeProgress1.setMinWidth(minimumWidth);
volumeProgress2.setMinWidth(masterVolumeSliderWidth - minimumWidth);
volumeProgress2.setMaxWidth(masterVolumeSliderWidth - minimumWidth);
}

}

/**
* @return the borderPane
*/
public BorderPane getBorderPane() {
return borderPane;
}

/**
* @return the masterVolumeSlider
*/
public Slider getMasterVolumeSlider() {
return masterVolumeSlider;
}

}

.css

.transparent-volume-progress-bar2-nostrip > .bar,.transparent-volume-progress-bar2-nostrip:indeterminate .bar,.transparent-volume-progress-bar2-nostrip:determinate .track,.transparent-volume-progress-bar2-nostrip:indeterminate .track{
-fx-accent:rgb(0.0, 144.0, 255.0);
-fx-background-color: -fx-accent;
-fx-background-radius:0.0;
-fx-border-radius:0.0;
}

.transparent-volume-progress-bar3-nostrip > .bar,.transparent-volume-progress-bar3-nostrip:indeterminate .bar,.transparent-volume-progress-bar3-nostrip:determinate .track,.transparent-volume-progress-bar3-nostrip:indeterminate .track{
-fx-accent:#fc4f4f;
-fx-background-color: -fx-accent;
-fx-background-radius:0.0;
-fx-border-radius:0.0;
}

.progress-bar > .bar {
-fx-accent:firebrick;
/*-fx-background-color:firebrick;*/
-fx-background-color: linear-gradient(
from 0.0px 0.75em to 0.75em 0.0px,
repeat,
-fx-accent 0.0%,
-fx-accent 49.0%,
derive(-fx-accent, 30.0%) 50.0%,
derive(-fx-accent, 30.0%) 99.0%
);

-fx-background-insets: 3.0;
-fx-padding: 0.2em;
}

.transparent-progress-bar:determinate .track,.transparent-progress-bar:indeterminate .track{
-fx-background-color:rgb(0.0,0.0,0.0,0.5);
}

/* .transparent-progress-bar */
.transparent-progress-bar > .bar,.transparent-progress-bar:indeterminate .bar{
-fx-accent:firebrick;
}

How to maintain the background color while adding an image in JavaFX?

You can only set a single fill on a Rectangle. Setting the fill to a different value replaces the old fill instead of "adding" to it.

You should use a pane with a background instead and add a ImageView to it, e.g.:

@Override
public void start(Stage primaryStage) throws Exception {
final double size = 50;

ImageView pawn = new ImageView("https://upload.wikimedia.org/wikipedia/commons/9/93/Chess_tile_pd.png");
pawn.setFitWidth(size);
pawn.setFitHeight(size);

final Background grey = new Background(new BackgroundFill(Color.GRAY, CornerRadii.EMPTY, Insets.EMPTY));
final Background white = new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY));

GridPane grid = new GridPane();
ColumnConstraints columnConstraints = new ColumnConstraints(size);
RowConstraints rowConstraints = new RowConstraints(size);
for (int i = 0; i < 8; i++) {
grid.getColumnConstraints().add(columnConstraints);
grid.getRowConstraints().add(rowConstraints);

for (int j = 0; j < 8; j++) {
StackPane field = new StackPane();
field.setBackground(((i + j) & 1) == 0 ? white : grey);
grid.add(field, i, j);
field.setOnMouseClicked(evt -> {
if (pawn.getParent() != field) {
field.getChildren().add(pawn); // move pawn to this field on click
}
});
}
}

((Pane) grid.getChildren().get(0)).getChildren().add(pawn);

Scene scene = new Scene(grid);

primaryStage.setScene(scene);
primaryStage.show();
}

How to set a TableRow's background colour based on whether or not it's selected and/or a value in the data model, in a JavaFX8 TableView?

There are a couple ways you can do this. Here's an example using external CSS and pseudo-classes:

Main.java

import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

@Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>(createDummyData(100));
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

table.setRowFactory(t -> new ItemTableRow());

TableColumn<Item, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
table.getColumns().add(nameCol);

TableColumn<Item, Boolean> validCol = new TableColumn<>("Valid");
validCol.setCellValueFactory(features -> features.getValue().validProperty());
table.getColumns().add(validCol);

primaryStage.setScene(new Scene(new StackPane(table), 800, 600));
primaryStage.getScene().getStylesheets().add(getClass().getResource("Main.css").toExternalForm());
primaryStage.setTitle("JavaFX Application");
primaryStage.show();
}


private ObservableList<Item> createDummyData(int count) {
return IntStream.rangeClosed(1, count)
.mapToObj(i -> "Item #" + i)
.map(name -> new Item(name, Math.random() >= 0.5))
.collect(Collectors.toCollection(FXCollections::observableArrayList));
}

}

ItemTableRow.java

import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TableRow;

public class ItemTableRow extends TableRow<Item> {

private static final PseudoClass VALID = PseudoClass.getPseudoClass("valid");

private final ChangeListener<Boolean> listener = (obs, oldVal, newVal) -> updateValidPseudoClass(newVal);
private final WeakChangeListener<Boolean> weakListener = new WeakChangeListener<>(listener);

public ItemTableRow() {
getStyleClass().add("item-table-row");
}

@Override
protected void updateItem(Item item, boolean empty) {
Item oldItem = getItem();
if (oldItem != null) {
oldItem.validProperty().removeListener(weakListener);
}
super.updateItem(item, empty);
if (empty || item == null) {
updateValidPseudoClass(false);
} else {
item.validProperty().addListener(weakListener);
updateValidPseudoClass(item.isValid());
}
}

private void updateValidPseudoClass(boolean active) {
pseudoClassStateChanged(VALID, active);
}

}

Item.java

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Item {

private final StringProperty name = new SimpleStringProperty(this, "name");
public final void setName(String name) { this.name.set(name); }
public final String getName() { return name.get(); }
public final StringProperty nameProperty() { return name; }

private final BooleanProperty valid = new SimpleBooleanProperty(this, "valid");
public final void setValid(boolean valid) { this.valid.set(valid); }
public final boolean isValid() { return valid.get(); }
public final BooleanProperty validProperty() { return valid; }

public Item() {}

public Item(String name, boolean valid) {
setName(name);
setValid(valid);
}

}

Main.css

.item-table-row:selected {
-fx-background-color: -fx-control-inner-background, green;
}

.item-table-row:valid {
-fx-background-color: -fx-control-inner-background, yellow;
}

.item-table-row:valid:selected {
-fx-background-color: -fx-control-inner-background, red;
}

If you prefer to only use code, change ItemTableRow to this (and remove getStylesheets().add(...) from Main):

import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.scene.control.TableRow;

public class ItemTableRow extends TableRow<Item> {

private final InvalidationListener listener = observable -> updateStyle();
private final WeakInvalidationListener weakListener = new WeakInvalidationListener(listener);

public ItemTableRow() {
getStyleClass().add("item-table-row");
selectedProperty().addListener(listener); // could also override updateSelected
}

@Override
protected void updateItem(Item item, boolean empty) {
Item oldItem = getItem();
if (oldItem != null) {
oldItem.validProperty().removeListener(weakListener);
}
super.updateItem(item, empty);
if (item != null) {
item.validProperty().addListener(weakListener);
}
updateStyle();
}

private void updateStyle() {
final Item item = getItem();
if (item == null || (!isSelected() && !item.isValid())) {
setStyle(null);
} else if (isSelected() && item.isValid()) {
setStyle("-fx-background-color: -fx-control-inner-background, red;");
} else if (isSelected()) {
setStyle("-fx-background-color: -fx-control-inner-background, green;");
} else if (item.isValid()) {
setStyle("-fx-background-color: -fx-control-inner-background, yellow;");
} else {
// I don't think this branch is possible, but not 100% sure
throw new AssertionError("Shouldn't be here?");
}
}

}

The -fx-control-inner-background value is defined in modena.css (the default stylesheet for JavaFX 8+). It gives the TableRow that little bit of color padding.



Related Topics



Leave a reply



Submit