JavaFX 重复子级饼图

min*_*123 5 java javafx

我正在编写一个程序,其中饼图中的按钮单击数据旋转(10-12 点钟方向的切片移动到 12-2 等)。下面的代码(有点)有效,它会旋转,但会吃掉临时切片并产生整段错误。这是我第一次尝试 JavaFX,我不确定如何管理它。

    private BorderPane layout;
    private Scene scene;

    ObservableList<PieChart.Data> pieChartData =
            FXCollections.observableArrayList(
                    new PieChart.Data("Post-production age", 424236),
                    new PieChart.Data("Production age", 1030060),
                    new PieChart.Data("Production age2", 1030060),
                    new PieChart.Data("Production age3", 1030060),
                    new PieChart.Data("Pre-production age", 310319));
    PieChart chart = new PieChart(pieChartData);

    @Override public void start(Stage stage) {
        layout = new BorderPane();
        scene = new Scene(layout,720,480);
        stage.setTitle("People");
        stage.setWidth(500);
        stage.setHeight(500);

        Button button = new Button();
        button.setText("rotate");
        layout.setBottom(button);
        layout.setCenter(chart);
        button.setOnAction(e -> {
            rotate();
        });

        chart.setStartAngle(90);
        chart.setTitle("Economical age groups");
        stage.setScene(scene);
        stage.show();
    }
    public  void rotate(){

        ObservableList<PieChart.Data> pieChartDataTemp = pieChartData;
        int sizeOne = pieChartDataTemp.size();
        PieChart.Data tempData = pieChartDataTemp.get(sizeOne-1);
         pieChartDataTemp.add(0,tempData);
         if(pieChartDataTemp.size()>sizeOne) pieChartDataTemp.remove(pieChartDataTemp.size()-1 );

        PieChart chartTemp = new PieChart(pieChartDataTemp);
        layout.setCenter(chartTemp);
        chartTemp.setStartAngle(90);

    }
Run Code Online (Sandbox Code Playgroud)

这是堆栈跟踪:

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Children: duplicate children added: parent = Chart$1@5cfeee33[styleClass=chart-content]
    at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:558)
    at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
    at javafx.controls/javafx.scene.chart.PieChart.dataItemAdded(PieChart.java:417)
    at javafx.controls/javafx.scene.chart.PieChart.lambda$new$0(PieChart.java:168)
    at javafx.base/com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
    at javafx.base/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.base/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at javafx.base/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.base/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.base/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
    at javafx.base/javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:155)
    at task.Main.rotate(Main.java:54)
    at task.Main.lambda$start$0(Main.java:39)
(...and so on)
Run Code Online (Sandbox Code Playgroud)

Sla*_*law 5

一些背景

JavaFX 图表支持添加、删除或更新数据时的动画。但是,当启用动画时,用于实际显示数据节点在动画完成之前不会从图表中删除 - 这不是公开可见的。这意味着您不能PieChart.Data像您目前所做的那样,只删除 a然后立即重新添加它。尝试这样做会导致尝试将 a 添加Node到 aParent时,当 saidNode仍然当前是 said 的孩子时Parent。正如IllegalArgumentException你所说,不允许重复的孩子


代码中的问题

你有这个代码(来自#rotate()):

ObservableList<PieChart.Data> pieChartDataTemp = pieChartData;
int sizeOne = pieChartDataTemp.size();
PieChart.Data tempData = pieChartDataTemp.get(sizeOne - 1);
pieChartDataTemp.add(0,tempData);
if (pieChartDataTemp.size() > sizeOne) {
    pieChartDataTemp.remove(pieChartDataTemp.size() - 1);
}

PieChart chartTemp = new PieChart(pieChartDataTemp);
layout.setCenter(chartTemp);
chartTemp.setStartAngle(90);
Run Code Online (Sandbox Code Playgroud)

两者pieChartDataTemppieChartData指代相同ObservableList。因此,当您tempData0索引处添加时,您实际上是将元素添加到当前元素,PieChart而它仍然存在于size() - 1索引处。我试图通过交换addremove调用来解决这个问题,但这并没有解决问题——这就是我发现动画阻碍的原因。

您还可以创建一个新的PieChart并替换旧的。这不是必需的,假设我正确理解你想要做什么。它也可能会导致问题,因为您ObservableList对每个PieChart.


解决方案

至少有两种解决方案。

禁用动画

一种解决方法是简单地禁用动画

yourPieChart.setAnimated(false);
Run Code Online (Sandbox Code Playgroud)

复制 PieChart.Data

另一种选择是PieChart.Data从旧的创建一个新的。下面是一个例子:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.control.Separator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

    private ObservableList<PieChart.Data> createChartData() {
        return FXCollections.observableArrayList(
                new PieChart.Data("Post-production age", 424236),
                new PieChart.Data("Production age", 1030060),
                new PieChart.Data("Production age2", 1030060),
                new PieChart.Data("Production age3", 1030060),
                new PieChart.Data("Pre-production age", 310319)
        );
    }

    @Override
    public void start(Stage primaryStage) {
        PieChart chart = new PieChart(createChartData());
        chart.setStartAngle(90.0);

        Button rotateBtn = new Button("Rotate");
        rotateBtn.setOnAction(event -> {
            event.consume();
            PieChart.Data removed = chart.getData().remove(chart.getData().size() - 1);
            chart.getData().add(0, new PieChart.Data(removed.getName(), removed.getPieValue()));
        });

        VBox root = new VBox(10, rotateBtn, new Separator(), chart);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(20));

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

}
Run Code Online (Sandbox Code Playgroud)

旋转代码位于onAction按钮的处理程序中:

rotateBtn.setOnAction(event -> {
    event.consume();
    PieChart.Data removed = chart.getData().remove(chart.getData().size() - 1);
    chart.getData().add(0, new PieChart.Data(removed.getName(), removed.getPieValue()));
});
Run Code Online (Sandbox Code Playgroud)

注意我没有创建另一个PieChart.