package mj.ocraptor.javafx; import javafx.collections.ObservableList; import javafx.geometry.Bounds; import javafx.scene.Node; import javafx.scene.chart.PieChart; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; public class DoughnutChart extends PieChart { private final Circle innerCircle; public DoughnutChart(ObservableList<Data> pieData) { super(pieData); innerCircle = new Circle(); // just styled in code for demo purposes, // use a style class instead to style via css. innerCircle.setFill(Color.WHITESMOKE); innerCircle.setStroke(Color.WHITE); innerCircle.setStrokeWidth(3); } @Override protected void layoutChartChildren(double top, double left, double contentWidth, double contentHeight) { super.layoutChartChildren(top, left, contentWidth, contentHeight); addInnerCircleIfNotPresent(); updateInnerCircleLayout(); } private void addInnerCircleIfNotPresent() { if (getData().size() > 0) { Node pie = getData().get(0).getNode(); if (pie.getParent() instanceof Pane) { Pane parent = (Pane) pie.getParent(); if (!parent.getChildren().contains(innerCircle)) { parent.getChildren().add(innerCircle); } } } } private void updateInnerCircleLayout() { double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE; double maxX = Double.MIN_VALUE, maxY = Double.MIN_VALUE; for (PieChart.Data data : getData()) { Node node = data.getNode(); Bounds bounds = node.getBoundsInParent(); if (bounds.getMinX() < minX) { minX = bounds.getMinX(); } if (bounds.getMinY() < minY) { minY = bounds.getMinY(); } if (bounds.getMaxX() > maxX) { maxX = bounds.getMaxX(); } if (bounds.getMaxY() > maxY) { maxY = bounds.getMaxY(); } } innerCircle.setCenterX(minX + (maxX - minX) / 2); innerCircle.setCenterY(minY + (maxY - minY) / 2); innerCircle.setRadius((maxX - minX) / 4); } }