OriolesMagic
OriolesMagic

Reputation: 63

Creating Text inside Bubble of JavaFX BubbleChart

I have a JavaFX BubbleChart in my visualization and I need to be able to create/display text within each bubble of the chart. In my visualization, I have numerous XYChart.Series with only 1 bubble per series. For each series, I do "series.setName("xxxx");" (where xxxx = unique series name) and I need to be able to display that series name inside the bubble.

I already have implemented a Tooltip (mouse-over event) for the Bubble Chart that displays the series name, but I need to also have the text visible inside the bubble without requiring a mouse-over.

For the sake of having code to work against, here is a basic example with 5 series. How would I go about adding a text inside each Bubble?

Thank you.

public class bubbleChartTest extends Application {

@Override
public void start(Stage stage) {

    final NumberAxis xAxis = new NumberAxis(0, 10, 1);
    final NumberAxis yAxis = new NumberAxis(0, 10, 1);
    final BubbleChart<Number, Number> bc = new BubbleChart<Number, Number>(xAxis, yAxis);

    xAxis.setLabel("X Axis");
    xAxis.setMinorTickCount(2);
    yAxis.setLabel("Y Axis");
    yAxis.setTickLabelGap(2);

    bc.setTitle("Bubble Chart StackOverflow Example");

    XYChart.Series<Number, Number> series1 = new XYChart.Series<Number, Number>();
    series1.setName("Series 1");
    series1.getData().add(new XYChart.Data<Number, Number>(3, 7, 1.5));

    XYChart.Series<Number, Number> series2 = new XYChart.Series<Number, Number>();
    series2.setName("Series 2");
    series2.getData().add(new XYChart.Data<Number, Number>(8, 3, 1));

    XYChart.Series<Number, Number> series3 = new XYChart.Series<Number, Number>();
    series3.setName("Series 3");
    series3.getData().add(new XYChart.Data<Number, Number>(1, 9, 2));

    XYChart.Series<Number, Number> series4 = new XYChart.Series<Number, Number>();
    series4.setName("Series 4");
    series4.getData().add(new XYChart.Data<Number, Number>(4, 1, 0.5));

    XYChart.Series<Number, Number> series5 = new XYChart.Series<Number, Number>();
    series5.setName("Series 5");
    series5.getData().add(new XYChart.Data<Number, Number>(9, 9, 3));

    Scene scene = new Scene(bc);
    bc.getData().addAll(series1, series2, series3, series4, series5);
    stage.setScene(scene);
    stage.show();

    for(XYChart.Series<Number, Number> series : bc.getData()) {
        for(XYChart.Data<Number, Number> data : series.getData()) {
            Tooltip.install(data.getNode(), new Tooltip(series.getName()));
        }
    }
}

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

}

Upvotes: 2

Views: 793

Answers (1)

aw-think
aw-think

Reputation: 4803

The node you get from the data is a Stackpane, and the Stackpane is shaped as an Ellipse. You probably need the radius of that Ellipse in x orientation and add a label to the Stackpane. But you need to set the minWidth Property of the Label, otherwise it will only display the three dots. And you need a Property to hold a dynamic font size, because it should look pretty if you want to resize the chart.

You do not need much code to get this work:

    for (XYChart.Series<Number, Number> series : bc.getData()) {
        for (XYChart.Data<Number, Number> data : series.getData()) {
            Node bubble = data.getNode();
            if (bubble != null && bubble instanceof StackPane) {
                StackPane region = (StackPane) bubble;
                if (region.getShape() != null && region.getShape() instanceof Ellipse) {
                    Ellipse ellipse = (Ellipse) region.getShape();
                    DoubleProperty fontSize = new SimpleDoubleProperty(10);

                    Label label = new Label(series.getName());
                    label.setAlignment(Pos.CENTER);
                    label.minWidthProperty().bind(ellipse.radiusXProperty());
                    //fontSize.bind(Bindings.when(ellipse.radiusXProperty().lessThan(40)).then(6).otherwise(10));
                    fontSize.bind(Bindings.divide(ellipse.radiusXProperty(), 5));
                    label.styleProperty().bind(Bindings.concat("-fx-font-size:", fontSize.asString(), ";"));
                    region.getChildren().add(label);
                }
            }
        }
    }

Application

Update

James_D mentioned that the loop isn't much robust in case of changing the, for ex., Shape. So I've changed it a bit to ask for the ellipse instance. This is a bit like the original layoutPlotChildren method from BubbleChart.

Upvotes: 2

Related Questions