如何在Java中实时更新XYChart(使用JavaFX)?
我正在使用JavaFX和JavaFXSDK中的散点图XYChart在Java中创建不同排序算法的可视化。我的想法是,在排序算法中进行交换后,每次都会重新绘制散点图。我从一个简单版本的insertionSort开始,因为它似乎是最容易实现的算法 我的问题是,当我开始排序时,程序会锁定,直到排序完全完成,并完全重新绘制图形。我希望它在每一步都重新绘制图表!我添加了一个Thread.sleepx,然后切换到TimeUnit.millizes.sleepx,尝试添加延迟,但遗憾的是,这并没有解决问题,它只是增加了程序锁定的时间,直到排序完成后才重新绘制图形。有没有一种方法可以在不改变JavaFX框架的情况下实现我想要的功能 包装样品; 导入javafx.application.application; 导入javafx.collections.FXCollections; 导入javafx.event.ActionEvent; 导入javafx.event.EventHandler; 导入javafx.fxml.fxmloader; 导入javafx.scene.Group; 导入javafx.scene.Parent; 导入javafx.scene.scene; 导入javafx.scene.chart.*; 导入javafx.scene.control.Button; 导入javafx.scene.layout.GridPane; 导入javafx.stage.stage; 导入jdk.jfr.Event; 导入java.util.array; 导入java.util.Random; 导入java.util.concurrent.TimeUnit; 公共类主扩展应用程序{ 按钮sortBtn=新按钮; @凌驾 public void startStage primaryStage引发异常{ //创建条形图 int size=100; int maxValue=100; int windowX=1920; int windowY=1020; int[]unsortedArray=createunsortedarray大小,最大值; NumberAxis xAxis=新的NumberAxis; xAxis.setLabelPosition; NumberAxis yAxis=新的NumberAxis; yAxis.setLabelValue; XYChart.Series如何在Java中实时更新XYChart(使用JavaFX)?,java,algorithm,javafx,Java,Algorithm,Javafx,我正在使用JavaFX和JavaFXSDK中的散点图XYChart在Java中创建不同排序算法的可视化。我的想法是,在排序算法中进行交换后,每次都会重新绘制散点图。我从一个简单版本的insertionSort开始,因为它似乎是最容易实现的算法 我的问题是,当我开始排序时,程序会锁定,直到排序完全完成,并完全重新绘制图形。我希望它在每一步都重新绘制图表!我添加了一个Thread.sleepx,然后切换到TimeUnit.millizes.sleepx,尝试添加延迟,但遗憾的是,这并没有解决问题,它
查看更多详细信息,并阅读。任何非琐碎的、与UI无关的进程都应该在单独的计算机上运行 线您可以创建一个任务并根据需要设置其不同的属性,然后将其放置在线程中并运行该线程。您只应使用task.setOnRunning、task.setOnSucceeded和task.setOnFailed方法更新UI:
查看更多详细信息,并阅读。JavaFX应用程序线程负责呈现UI和处理事件。如果事件处理程序运行时间较长,则在处理程序完成之前无法呈现UI。因此,永远不要执行长时间运行的任务,包括在事件处理程序中睡眠或在FX应用程序线程上执行的代码中的任何位置 在您的示例中,您希望在特定时间点更新UI,并且为每个单独步骤执行的代码不会花费很长时间来运行。在这种情况下,最好的方法是动画,如时间线。重构代码需要一点工作,这样迭代的每个步骤都可以单独运行,但最干净的方法是创建一个表示排序的类,并使用一个方法一次执行一个步骤:
private static class InsertionSort {
private int lowPos = 0;
private int[] data ;
private int i ; // current iteration
InsertionSort(int[] unsortedArray) {
this.data = unsortedArray ;
}
private boolean isDone() {
return i >= data.length ;
}
private int[] nextStep() {
if (isDone()) throw new IllegalStateException("Sorting is complete");
lowPos = i;
for (int j = i; j < data.length; j++) {
if(data[j] < data[lowPos]){
lowPos = j;
}
}
//Swap lowPos value with i
int swappedValue = data[i];
data[i] = data[lowPos];
data[lowPos] = swappedValue;
i++ ;
return data ;
}
}
以下是以这种方式重构的完整示例注意:我还关闭了图表的默认动画,这将无法与自定义动画配合使用:
import java.util.Random;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
Button sortBtn = new Button();
@Override
public void start(Stage primaryStage) throws Exception{
//Creating Bar Graph
int size = 100;
int maxValue = 100;
int windowX = 1920;
int windowY = 1020;
int[] unsortedArray = createUnsortedArray(size, maxValue);
NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Position");
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Value");
XYChart.Series<Number, Number> graph = new XYChart.Series<>();
for(int i = 0; i < size; i++){
graph.getData().add(new XYChart.Data<>(i, unsortedArray[i]));
}
ScatterChart<Number, Number> scatterChart = new ScatterChart<>(xAxis, yAxis);
scatterChart.setTitle("Unsorted Array");
scatterChart.setAnimated(false);
scatterChart.getData().addAll(graph);
scatterChart.setPrefSize(windowX-100,windowY);
//End Creating Bar Graph
primaryStage.setTitle("Sort Visualizer");
GridPane layout = new GridPane();
layout.getChildren().add(0,scatterChart);
sortBtn = new Button("Sort");
layout.getChildren().add(1,sortBtn);
Scene scene = new Scene(layout, windowX,windowY);
primaryStage.setScene(scene);
primaryStage.show();
sortBtn.setOnAction(actionEvent -> {
try {
insertionSort(graph,unsortedArray);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
public void insertionSort(XYChart.Series<Number, Number> graph, int[] unsortedArray) throws InterruptedException {
InsertionSort sort = new InsertionSort(unsortedArray);
Timeline timeline = new Timeline();
timeline.getKeyFrames().add(
new KeyFrame(Duration.millis(100), event -> {
updateGraph(graph, sort.nextStep());
if (sort.isDone()) {
timeline.stop();
}
})
);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
public void updateGraph( XYChart.Series<Number, Number> graph, int[] updatedArray){
graph.getData().clear();
for(int i = 0; i < updatedArray.length; i++){
graph.getData().add(new XYChart.Data<>(i, updatedArray[i]));
}
}
int[] createUnsortedArray(int size, int maxValue){
int[] unsortedArray = new int[size];
Random randy = new Random();
for(int i = 0; i < size; i++){
unsortedArray[i] = Math.abs(randy.nextInt() % maxValue);
}
return unsortedArray;
}
private static class InsertionSort {
private int lowPos = 0;
private int[] data ;
private int i ; // current iteration
InsertionSort(int[] unsortedArray) {
this.data = unsortedArray ;
}
private boolean isDone() {
return i >= data.length ;
}
private int[] nextStep() {
if (isDone()) throw new IllegalStateException("Sorting is complete");
lowPos = i;
for (int j = i; j < data.length; j++) {
if(data[j] < data[lowPos]){
lowPos = j;
}
}
//Swap lowPos value with i
int swappedValue = data[i];
data[i] = data[lowPos];
data[lowPos] = swappedValue;
i++ ;
return data ;
}
}
public static void main(String[] args) {
launch(args);
}
}
JavaFX应用程序线程负责呈现UI和处理事件。如果事件处理程序运行时间较长,则在处理程序完成之前无法呈现UI。因此,永远不要执行长时间运行的任务,包括在事件处理程序中睡眠或在FX应用程序线程上执行的代码中的任何位置 在您的示例中,您希望在特定时间点更新UI,并且为每个单独步骤执行的代码不会花费很长时间来运行。在这种情况下,最好的方法是动画,如时间线。重构代码需要一点工作,这样迭代的每个步骤都可以单独运行,但最干净的方法是创建一个表示排序的类,并使用一个方法一次执行一个步骤:
private static class InsertionSort {
private int lowPos = 0;
private int[] data ;
private int i ; // current iteration
InsertionSort(int[] unsortedArray) {
this.data = unsortedArray ;
}
private boolean isDone() {
return i >= data.length ;
}
private int[] nextStep() {
if (isDone()) throw new IllegalStateException("Sorting is complete");
lowPos = i;
for (int j = i; j < data.length; j++) {
if(data[j] < data[lowPos]){
lowPos = j;
}
}
//Swap lowPos value with i
int swappedValue = data[i];
data[i] = data[lowPos];
data[lowPos] = swappedValue;
i++ ;
return data ;
}
}
以下是以这种方式重构的完整示例注意:我还关闭了图表的默认动画,这将无法与自定义动画配合使用:
import java.util.Random;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
Button sortBtn = new Button();
@Override
public void start(Stage primaryStage) throws Exception{
//Creating Bar Graph
int size = 100;
int maxValue = 100;
int windowX = 1920;
int windowY = 1020;
int[] unsortedArray = createUnsortedArray(size, maxValue);
NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Position");
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Value");
XYChart.Series<Number, Number> graph = new XYChart.Series<>();
for(int i = 0; i < size; i++){
graph.getData().add(new XYChart.Data<>(i, unsortedArray[i]));
}
ScatterChart<Number, Number> scatterChart = new ScatterChart<>(xAxis, yAxis);
scatterChart.setTitle("Unsorted Array");
scatterChart.setAnimated(false);
scatterChart.getData().addAll(graph);
scatterChart.setPrefSize(windowX-100,windowY);
//End Creating Bar Graph
primaryStage.setTitle("Sort Visualizer");
GridPane layout = new GridPane();
layout.getChildren().add(0,scatterChart);
sortBtn = new Button("Sort");
layout.getChildren().add(1,sortBtn);
Scene scene = new Scene(layout, windowX,windowY);
primaryStage.setScene(scene);
primaryStage.show();
sortBtn.setOnAction(actionEvent -> {
try {
insertionSort(graph,unsortedArray);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
public void insertionSort(XYChart.Series<Number, Number> graph, int[] unsortedArray) throws InterruptedException {
InsertionSort sort = new InsertionSort(unsortedArray);
Timeline timeline = new Timeline();
timeline.getKeyFrames().add(
new KeyFrame(Duration.millis(100), event -> {
updateGraph(graph, sort.nextStep());
if (sort.isDone()) {
timeline.stop();
}
})
);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
public void updateGraph( XYChart.Series<Number, Number> graph, int[] updatedArray){
graph.getData().clear();
for(int i = 0; i < updatedArray.length; i++){
graph.getData().add(new XYChart.Data<>(i, updatedArray[i]));
}
}
int[] createUnsortedArray(int size, int maxValue){
int[] unsortedArray = new int[size];
Random randy = new Random();
for(int i = 0; i < size; i++){
unsortedArray[i] = Math.abs(randy.nextInt() % maxValue);
}
return unsortedArray;
}
private static class InsertionSort {
private int lowPos = 0;
private int[] data ;
private int i ; // current iteration
InsertionSort(int[] unsortedArray) {
this.data = unsortedArray ;
}
private boolean isDone() {
return i >= data.length ;
}
private int[] nextStep() {
if (isDone()) throw new IllegalStateException("Sorting is complete");
lowPos = i;
for (int j = i; j < data.length; j++) {
if(data[j] < data[lowPos]){
lowPos = j;
}
}
//Swap lowPos value with i
int swappedValue = data[i];
data[i] = data[lowPos];
data[lowPos] = swappedValue;
i++ ;
return data ;
}
}
public static void main(String[] args) {
launch(args);
}
}
您需要将排序结果发送到JavaFX线程以更新UI。。。不要使用睡眠!!!!阅读有关Runnable、Task、Platform.runLater的信息。关于这一点,这里有很多答案。但是请注意,对于这类功能,动画API是一个使用后台线程的更合适的解决方案。我有点困惑为什么这是插入排序而不是选择排序。它不是找到最低的值并将其放在前面,而不是在每个值到达时进行排序吗?您需要将排序结果发送到JavaFX线程以更新UI。。。不要使用睡眠!!!!阅读有关Runnable、Task、Platform.runLater的信息。关于这一点,这里有很多答案。但是请注意,对于这类功能,动画API是一个使用后台线程的更合适的解决方案。我有点困惑为什么这是插入排序而不是选择排序。它不是找到最低的值并将其放在前面,而不是在每个值到达时对其进行排序吗?