JavaFX Double Donut图表

Che*_*ple 7 javafx pie-chart donut-chart

我在离线环境中工作,因此无法复制整个代码库,我将尽我所能描述。

JavaFX不提供甜甜圈图,因此我通过简单地扩展PieChart然后在中心添加一个圆圈来创建了自定义甜甜圈图。伪代码如下所示:

public class DonutChart extends PieChart{
    private final Circle innerCircle;

    //Initialize PieChart
    //Override "layoutChartChildren()" method to override chart label with value instead of name
    //Calculate chart bounds by looping through each node to find the max/min X/Y bounds.
    //Create a smaller circle and relocate it to the center of the PieChart
}
Run Code Online (Sandbox Code Playgroud)

这给了我想要的甜甜圈图,但是现在我希望有一个双甜甜圈图,这意味着可以进一步细分图表。我再次做了同样的事情,但是这次,我不仅添加了“ innerCircle”,还添加了另一个饼图。现在看起来像这样:

public class DonutChart extends PieChart{
    private final Circle innerCircle;
    private PieChart innerChart;

   /*
    * Initialize PieChart
    * Override "layoutChartChildren()" method to override chart label with value instead of name
    * Calculate chart bounds by looping through each node to find the max/min X/Y bounds.
    * Create and add innerChart and innerCircle
    */
    private void addChartAndCircle(){
        if (getData().size > 0){
            Node pie = getData().get(0).getNode();
            if (pie.getParent() instanceof Pane){
                Pane parent = pie.getParent();
                if (!parent.getChildren().contains(innerChart)){
                    parent.getChildren().add(innerChart);
                }
                if (!parent.getChildren().contains(innerCircle)){
                    parent.getChildren().add(innerCircle);
                }
            }
        }
    }
    //Relocate to center
}
Run Code Online (Sandbox Code Playgroud)

仅显示外部图表“ DonutChart”和内部圆圈,但看不到innerChart。我还观察到内部图表的width属性为0,并且我尝试绑定该属性,将innerChart的max / min / pref width / height设置为无效,但无济于事。

有谁能够创建显示两层的自定义甜甜圈图?

双甜甜圈图图

Sai*_*dem 8

由于这个问题看起来很有趣,因此我尝试了您的要求。确实,我也遇到了渲染内部图表的确切问题。经过进一步调查,看起来问题出在区域的子布局中。

现在,我们正在介绍一个陌生人(又名内部图表),外部图表的layoutChildren()不知道如何呈现它。您可能会问,圆是如何渲染的。我认为Shapes的工作方式会有所不同。不确定 ;-)

因此,要使其工作,您需要在外部图表的layoutChartChildren()方法中提供内部图表的大小。

跳过一些您提供的内容,下面是我尝试使其工作的快速演示。希望对您有所帮助。

在此处输入图片说明

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

import java.util.Set;
import java.util.stream.Collectors;

public class PieChartSample extends Application {

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Imported Fruits");
        stage.setWidth(500);
        stage.setHeight(500);

        ObservableList<PieChart.Data> pieChartData =
                FXCollections.observableArrayList(
                        new PieChart.Data("Grapefruit", 45),
                        new PieChart.Data("Oranges", 25),
                        new PieChart.Data("Plums", 30));

        ObservableList<PieChart.Data> innerChartData =
                FXCollections.observableArrayList(
                        new PieChart.Data("G1", 20),
                        new PieChart.Data("G2", 25),
                        new PieChart.Data("O1", 10),
                        new PieChart.Data("O2", 15),
                        new PieChart.Data("P1", 15),
                        new PieChart.Data("P2", 15));
        final DonutChart chart = new DonutChart(pieChartData);
        chart.setTitle("Imported Fruits");
        chart.setInnerChartData(innerChartData);

        ((Group) scene.getRoot()).getChildren().add(chart);
        stage.setScene(scene);
        stage.show();
    }

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

    class DonutChart extends PieChart {
        private final double SPACE = 30; /* Gap between outer and inner pie*/
        private PieChart innerChart;
        private Circle circle;
        private Bounds outerPieBounds;

        public DonutChart(ObservableList<PieChart.Data> data) {
            super(data);
            innerChart = new PieChart() {
                @Override
                protected void layoutChartChildren(double top, double left, double contentWidth, double contentHeight) {
                    super.layoutChartChildren(top, left, contentWidth, contentHeight);
                    final Pane chartContent = (Pane) lookup(".chart-content");
                    chartContent.setPadding(new Insets(0));
                }
            };
            innerChart.setPadding(new Insets(0));
            innerChart.setLabelsVisible(false);
            innerChart.setLegendVisible(false);

            circle = new Circle(40);
            circle.setOpacity(1);
            circle.setStyle("-fx-fill:white;");
        }

        @Override
        protected void layoutChartChildren(double top, double left, double contentWidth, double contentHeight) {
            super.layoutChartChildren(top, left, contentWidth, contentHeight);
            final Pane chartContent = (Pane) lookup(".chart-content");
            if (!chartContent.getChildren().contains(innerChart)) {
                chartContent.getChildren().add(innerChart);
            }
            if (!chartContent.getChildren().contains(circle)) {
                chartContent.getChildren().add(circle);
            }
            updateOuterPieBounds();
            double cX = outerPieBounds.getMinX() + (outerPieBounds.getWidth() / 2);
            double cY = outerPieBounds.getMinY() + (outerPieBounds.getHeight() / 2);
            circle.setCenterX(cX);
            circle.setCenterY(cY);
            double innerSize = outerPieBounds.getWidth() - (2 * SPACE);
            innerChart.resize(innerSize, innerSize); // THIS WHERE YOUR ISSUE LIES. YOU NEED TO PROVIDE THE SIZE TO INNER CHART
            innerChart.setTranslateX(cX - innerChart.getWidth() / 2);
            innerChart.setTranslateY(cY - innerChart.getHeight() / 2);
        }

        public void setInnerChartData(ObservableList<PieChart.Data> data) {
            innerChart.setData(data);
        }

        /**
         * Determining the outer pie visual bounds.
         */
        private void updateOuterPieBounds() {
            Pane chartContent = (Pane) lookup(".chart-content");
            Set<Node> pieNodes = chartContent.getChildren().stream().filter(node -> node.getStyleClass().contains("chart-pie")).collect(Collectors.toSet());
            double minX = getWidth();
            double minY = getHeight();
            double maxX = 0, maxY = 0;
            for (Node pie : pieNodes) {
                Bounds pieBounds = pie.getBoundsInParent();
                minX = Math.min(minX, pieBounds.getMinX());
                minY = Math.min(minY, pieBounds.getMinY());
                maxX = Math.max(maxX, pieBounds.getMaxX());
                maxY = Math.max(maxY, pieBounds.getMaxY());
            }
            outerPieBounds = new BoundingBox(minX, minY, maxX - minX, maxY - minY);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)