JavaFX双层甜甜圈图表

7
我在离线环境中工作,因此无法复制整个代码库,我将尽力描述尽可能多的内容。
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
}

这给我提供了我想要的甜甜圈图表,但现在我希望有一个双层甜甜圈图表,也就是说,另外一层进一步分解了图表。我再次做了同样的事情,但这一次除了添加“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
}

只有外部的"DonutChart"图表和内圆一起显示,但内部图表不可见。我还观察到内部图表的宽度属性为0,并尝试绑定该属性、设置内部图表的最大/最小/首选宽度/高度,但都没有成功。
有人能够创建一个自定义的甜甜圈图表,显示2个层吗?

Double Donut Chart Illustration

1个回答

8
由于这个问题看起来很有趣,我尝试满足您的需求。事实上,我也遇到了内部图表呈现的确切问题。进一步调查发现,问题似乎在于区域的布局子项。
由于我们现在引入了一个陌生人(即内部图表),外部图表的layoutChildren()不知道如何呈现它。您可能会问为什么圆圈会被呈现。我认为用Shapes会有不同的工作方式。不确定 ;-)
因此,要使其正常工作,您需要在外部图表的layoutChartChildren()方法中提供内部图表的大小。
跳过您提供的某些内容,以下是我尝试让其工作的快速演示。我希望这可以帮助您。 enter image description here
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);
        }
    }
}

哦,哇!是的!看起来就是这样!我想知道为什么没有更多这样的例子哈哈。谢谢你!+1 的努力!干杯。 - Cherple

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接