定期修改JavaFX StackedBarChart CategoryAxis

时间:2014-03-07 09:42:23

标签: charts javafx

我正试图找到一种方法来更新JavaFX CategoryAxis()的类别。我制作了一个可观察的类别列表,并且他们也在plot()函数中进行了更新。但是,如果我尝试在系列中添加一个新项目,我会得到一个java.lang.IllegalStateException。虽然我知道,不是状态导致错误,而且动态添加似乎是问题所在。下面我附上了我的代码。

package application;

import java.text.SimpleDateFormat; 
import java.util.Arrays;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application {

    final XYChart.Series series1 = new XYChart.Series();
    final XYChart.Series series2 = new XYChart.Series();

    ObservableList<XYChart.Data> xyList1 = FXCollections.observableArrayList();
    ObservableList<XYChart.Data> xyList2 = FXCollections.observableArrayList();

    ObservableList<String> myXaxis = FXCollections.observableArrayList();

    int i;

    @Override public void start(Stage stage) {       
        stage.setTitle("Line Chart Sample");
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Month");
        final StackedBarChart<String,Number> lineChart = 
                new StackedBarChart<String,Number>(xAxis,yAxis);

        lineChart.setTitle("Woohoo, 2010");
        lineChart.setAnimated(false);


        series1.setName("Test 1");
        series2.setName("test 2");

        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                plot();
            }
        }, 0, 1000);

        Scene scene  = new Scene(lineChart,800,600);   

        xAxis.setCategories(myXaxis);


        XYChart.Series XYSeries1 = new XYChart.Series(xyList1);
        XYChart.Series XYSeries2 = new XYChart.Series(xyList2);

        lineChart.getData().addAll(XYSeries1,XYSeries2);

        i = 0;

        stage.setScene(scene);
        stage.show();
    }


    public void plot() {         
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        Date date = new Date();
        date.setTime(date.getTime() + i++ * 11111);

        myXaxis.add(dateFormat.format(date));
        System.out.println(myXaxis);
        // with the line below uncommented the application breaks. Without the x-axis is updated as intended.
        //xyList1.add(new XYChart.Data(dateFormat.format(date), Math.random()*10));
    }

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

1 个答案:

答案 0 :(得分:2)

问题 - JavaFX应用程序线程的场景图节点的传递修改

不要在主JavaFX应用程序线程之外修改场景节点(甚至是场景节点所依赖的可观察数据列表)(这是非法的 - 因为你收到的IllegalStateException状态)。

Timer线程不会在JavaFX应用程序线程上运行。

潜在修复

有几种方法可以解决这个问题:

  1. 继续使用Timer,但使用Platform.runLater围绕计时器中的plot()来电。
  2. 使用JavaFX动画框架(Timeline),它始终在JavaFX应用程序线程上运行所有代码。
  3. 在这两个选项中,我认为我更喜欢第二种选择,但两者都可以。

    计时器样式解决方案

    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            Platform.runLater(new Runnable() {
                @Override public void run() {
                    plot();
                }
            }); 
        }
    }, 0, 1000);
    

    时间线样式解决方案

    Timeline timeline = new Timeline(
        new KeyFrame(
          Duration.ZERO,
          new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent actionEvent) {
              plot();
            }
          }
        ),
        new KeyFrame(
          Duration.seconds(1)
        )
    );
    timeline.setCycleCount(Timeline.INDEFINITE);
    timeline.play();
    

    基于替代服务的解决方案

    我建议您阅读concurrency in JavaFX

    另一种方法是将TaskServiceScheduledExecutorService结合使用,并通过Platform.runLater()运行更新。然而,这种解决方案虽然复杂而灵活,但比您所描述的问题更加复杂,并且应该首选简单的基于时间轴或定时器的解决方案。如果每个脉冲导致执行耗时的算法或大量基于网络或文件的I / O,则更复杂的基于服务的解决方案是合适的。