ranef
ranef

Reputation: 121

Adding value labels to BarChart's bar

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

Answers (3)

Ahmed Al Mashaikhi
Ahmed Al Mashaikhi

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

negste
negste

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

Kevin Lafayette
Kevin Lafayette

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

Related Questions