Reputation: 121
I would like to add labels with the bar numeric value on the top or on the body of each bar in a BarChart
I searched both in the API and in the CSS reference guide but could not find any option to define that.
Is this supported in the current version of JavaFX charts?
Upvotes: 2
Views: 3026
Reputation: 11
Here my solution:
@FXML
private BarChart<String, Number> bChart;
@FXML
private CategoryAxis xAxis;
@FXML
private NumberAxis yAxis;
private final ObservableList<Person> people = FXCollections.observableArrayList(param -> new Observable[]{param.birthDateProperty()});
private final SortedList<Person> sortedList = new SortedList<>(people,(o1, o2) -> Integer.compare(o2.getAge(),o1.getAge()));
private final FilteredList<Person> filteredList = new FilteredList<>(sortedList,person -> true);
private final XYChart.Series<String,Number> series = new XYChart.Series<>();
private void setupBarChart(){
series.getData().addAll(
createChartData("Max",0.0,Color.RED),
createChartData("Min",0.0,Color.ORANGE),
createChartData("Avg",0.0,Color.GREEN)
);
bChart.getData().add(series);
setChartDataBindings();
}
private XYChart.Data<String,Number> createChartData(String label, double value, Color color){
XYChart.Data<String,Number> data = new XYChart.Data<>(label,value,color);
StackPane pane = new StackPane();
pane.setAlignment(Pos.TOP_CENTER);
pane.setStyle("-fx-background-color: #"+color.toString().substring(2));
Text text = new Text();
text.setFont(Font.font("Arial Narrow", FontWeight.BOLD,14));
text.textProperty().bind(data.YValueProperty().asString("%.2f"));
StackPane.setAlignment(text, Pos.TOP_CENTER);
StackPane.setMargin(text, new Insets(0));
pane.getChildren().add(text);
pane.paddingProperty().bind(Bindings.createObjectBinding(() ->
{
if(yAxis.getHeight() - pane.getHeight() >= text.prefHeight(1.0)+ 10){
return new Insets(-1.0 * text.prefHeight(1.0),0,0,0);
} else return new Insets(2,0,0,0);
},yAxis.heightProperty(),pane.heightProperty()));
text.fillProperty().bind(Bindings.createObjectBinding(()-> pane.getPadding().getTop()<0 ? Color.BLACK : Color.WHITE,pane.paddingProperty()));
data.setNode(pane);
return data;
}
private void setChartDataBindings() {
series.getData().get(0).YValueProperty().bind(Bindings.createDoubleBinding(()-> filteredList.stream().mapToDouble(Person::getAge).max().orElse(0.0),filteredList));
series.getData().get(1).YValueProperty().bind(Bindings.createDoubleBinding(()-> filteredList.stream().mapToDouble(Person::getAge).min().orElse(0.0),filteredList));
series.getData().get(2).YValueProperty().bind(Bindings.createDoubleBinding(()-> filteredList.stream().mapToDouble(Person::getAge).average().orElse(0.0),filteredList));
}
Upvotes: 0
Reputation: 323
How about this:
import javafx.scene.chart.Axis;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
class CustomStackedBarChart extends StackedBarChart<String, Number> {
public CustomStackedBarChart(CategoryAxis xAxis, Axis<Number> yAxis) {
super(xAxis, yAxis);
}
@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
for (Series<String, Number> series : getData()) {
for (Data<String, Number> data : series.getData()) {
StackPane bar = (StackPane) data.getNode();
final Text dataText = new Text(data.getYValue() + "");
bar.getChildren().add(dataText);
}
}
}
}
Upvotes: 3
Reputation: 57
I am trying to do the same thing. So far, this is what I have.
class CustomStackedBarChart extends StackedBarChart<String, Number> {
public CustomStackedBarChart( CategoryAxis xAxis, Axis<Number> yAxis ) {
super( xAxis, yAxis );
setAnimated( false );
setLegendVisible( false );
setTitleSide( Side.TOP );
setTitle( "SSPP Diurnal Symbol Rate" );
setMinHeight( 500 );
setMinWidth( 1000 );
}
@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
// Each individual Data element has its own bar.
// TODO add label to each bar.
for ( Series<String, Number> series : getData() ) {
for ( Data<String, Number> data : series.getData() ) {
StackPane bar = (StackPane) data.getNode();
Label label = null;
for ( Node node : bar.getChildrenUnmodifiable() ) {
LOGGER.debug( "Bar has child {}, {}.", node,
node.getClass() );
if ( node instanceof Label ) {
label = (Label) node;
break;
}
}
if ( label == null ) {
label = new Label( series.getName() );
label.setRotate( 90.0 );
bar.getChildren().add( label );
}
else {
label.setText( series.getName() );
}
}
}
}
}
Upvotes: 1