Skip to content

Instantly share code, notes, and snippets.

@jewelsea
Last active January 26, 2024 14:54
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jewelsea/5094893 to your computer and use it in GitHub Desktop.
Save jewelsea/5094893 to your computer and use it in GitHub Desktop.
Colors the the bars in a JavaFX BarChart depending upon the value of each bar's data.
/**
* file: colored-chart.css
* Place in same directory as DynamicallyColoredBarChartWithLabel.java
* Ensure that the build system copies this file to the build output directory.
*/
.root {
-fx-not-achieved: red;
-fx-achieved: green;
-fx-exceeded: blue;
}
.default-color0.chart-bar { -fx-bar-fill: -fx-not-achieved; }
.default-color1.chart-bar { -fx-bar-fill: -fx-achieved; }
.default-color2.chart-bar { -fx-bar-fill: -fx-exceeded; }
.level-legend {
-fx-padding: 10;
-fx-border-width: 2;
-fx-background-color: rgba(211, 211, 211, 0.5);
-fx-border-color: derive(rgba(211, 211, 211, 0.7), 10%);
}
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
* Displays a bar with a single series whose bars are different colors depending upon the bar value.
* A custom legend is created and displayed for the bar data.
* Bars in the chart are customized to include a text label of the bar's data value above the bar.
*/
public class DynamicallyColoredBarChartWithLabel extends Application {
@Override public void start(Stage stage) {
final CategoryAxis xAxis = new CategoryAxis();
xAxis.setLabel("Bars");
final NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Value");
final BarChart<String, Number> bc = new BarChart<>(xAxis, yAxis);
bc.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
for (int i = 0; i < 10; i++) {
final XYChart.Data<String, Number> data = new XYChart.Data("Value " + i, i);
data.nodeProperty().addListener(new ChangeListener<Node>() {
@Override public void changed(ObservableValue<? extends Node> ov, Node oldNode, final Node node) {
if (node != null) {
setNodeStyle(data);
displayLabelForData(data);
}
}
});
series1.getData().add(data);
}
bc.getData().add(series1);
LevelLegend legend = new LevelLegend();
legend.setAlignment(Pos.CENTER);
VBox chartWithLegend = new VBox();
chartWithLegend.getChildren().setAll(bc, legend);
VBox.setVgrow(bc, Priority.ALWAYS);
chartWithLegend.getStylesheets().add(getClass().getResource("colored-chart.css").toExternalForm());
stage.setScene(new Scene(chartWithLegend));
stage.setMinHeight(400);
stage.setMinWidth(400);
stage.show();
}
/** Change color of bar if value of i is <5 then red, if >5 then green if i>8 then blue */
private void setNodeStyle(XYChart.Data<String, Number> data) {
Node node = data.getNode();
if (data.getYValue().intValue() > 8) {
node.setStyle("-fx-bar-fill: -fx-exceeded;");
} else if (data.getYValue().intValue() > 5) {
node.setStyle("-fx-bar-fill: -fx-achieved;");
} else {
node.setStyle("-fx-bar-fill: -fx-not-achieved;");
}
}
/** places a text label with a bar's value above a bar node for a given XYChart.Data */
private void displayLabelForData(XYChart.Data<String, Number> data) {
final Node node = data.getNode();
final Text dataText = new Text(data.getYValue() + "");
node.parentProperty().addListener(new ChangeListener<Parent>() {
@Override public void changed(ObservableValue<? extends Parent> ov, Parent oldParent, Parent parent) {
Group parentGroup = (Group) parent;
parentGroup.getChildren().add(dataText);
}
});
node.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
@Override public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds bounds) {
dataText.setLayoutX(
Math.round(
bounds.getMinX() + bounds.getWidth() / 2 - dataText.prefWidth(-1) / 2
)
);
dataText.setLayoutY(
Math.round(
bounds.getMinY() - dataText.prefHeight(-1) * 0.5
)
);
}
});
}
/** A simple custom legend for a three valued chart. */
class LevelLegend extends GridPane {
LevelLegend() {
setHgap(10);
setVgap(10);
addRow(0, createSymbol("-fx-exceeded"), new Label("Exceeded"));
addRow(1, createSymbol("-fx-achieved"), new Label("Achieved"));
addRow(2, createSymbol("-fx-not-achieved"), new Label("Not Achieved"));
getStyleClass().add("level-legend");
}
/** Create a custom symbol for a custom chart legend with the given fillStyle style string. */
private Node createSymbol(String fillStyle) {
Shape symbol = new Ellipse(10, 5, 10, 5);
symbol.setStyle("-fx-fill: " + fillStyle);
symbol.setStroke(Color.BLACK);
symbol.setStrokeWidth(2);
return symbol;
}
}
public static void main(String[] args) { launch(args); }
}
@jewelsea
Copy link
Author

jewelsea commented Mar 5, 2013

Answer to StackOverflow Question: How to change color of a single bar java fx

@jewelsea
Copy link
Author

Answer to StackOverflow Question: How to display bar value on top of bar JavaFX

@jewelsea
Copy link
Author

Answer to StackOverflow Question: How to add 3 legend for a single series barchart

@angelicalleite
Copy link

Thank you for me help in JavaFX, very very much util your sample. Congratulations

@danieldacorso
Copy link

Hey, dude,

Your example is amazing! Saved my day, but I have a problem:

I got a XY barchart with 4 columns with the values 300, 32, 0 , 1.

Its working fine, but the column with value 300, the label isnt showing, because the 300 is hitting the "top" of the Y chart. Is there a workaround?

Thanks!

@jeremyhalin
Copy link

@jewelsea Labels on top of bars chart work fine but when I update data, old values stay on screen.. How can I remove them ?

@yasineryigit
Copy link

@jewelsea Labels on top of bars chart work fine but when I update data, old values stay on screen.. How can I remove them ?

Hi, I'm having same issue. Did you find a solution for it?

@euulu
Copy link

euulu commented Jan 26, 2024

@jewelsea Hi, thank you very much for this example!
I'm experiencing the same issues as @danieldacorso, @jeremyhalin and @yasineryigit. Would be grateful for any help on how to make this code snippet better.
Till now I've investigated that the content of the BarChart clipped with Region(as shown on the attached video). But I don't know how to change this clipping behavior. Adding padding to the top of the chart didn't help.
Video

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment