Multithreading JavaFX制作条形图在UI线程中延迟更改条形图
我得到了JavaFX主线程,在那里我创建了新的线程来扩展任务、排序和替换条。一切都很好,但我想在替换时做一些延迟(比如100ms),以显示一步一步的排序,可能是动画。问题是,当我使用Thread.sleep()或TranslateTransition()时,它只是将所有延迟(毫秒)加在一起,形成一个大延迟,这个延迟发生在更改条之前。如何使延迟在UI线程中正常工作 在主要课堂上:Multithreading JavaFX制作条形图在UI线程中延迟更改条形图,multithreading,sorting,user-interface,javafx,task,Multithreading,Sorting,User Interface,Javafx,Task,我得到了JavaFX主线程,在那里我创建了新的线程来扩展任务、排序和替换条。一切都很好,但我想在替换时做一些延迟(比如100ms),以显示一步一步的排序,可能是动画。问题是,当我使用Thread.sleep()或TranslateTransition()时,它只是将所有延迟(毫秒)加在一起,形成一个大延迟,这个延迟发生在更改条之前。如何使延迟在UI线程中正常工作 在主要课堂上: Sorting sorting = new Sorting(); sortThread =
Sorting sorting = new Sorting();
sortThread = new Thread(sorting, "sort");
sortThread.start();
sortThread.join();
我的类排序扩展了这个任务
public class Sorting extends Task<Void> {
//some stuff here
@Override
protected Void call() throws Exception {
taskThread = new Thread(counter, "time");
taskThread.setDaemon(true);
taskThread.start();
int n = array_tmp.length;
int temp;
for (int i = 0; i < n; i++) {
for (int j = 1; j < (n - i); j++) {
if (array_tmp[j - 1] > array_tmp[j]) {
//replacing bars
Node n1 = barChart.getData().get(j-1).getData().get(0).getNode();
Node n2 = barChart.getData().get(j).getData().get(0).getNode();
double x1 = n1.getTranslateX() + ((barChart.getWidth()-69)/array_tmp.length);
double x2 = n2.getTranslateX() - ((barChart.getWidth()-69)/array_tmp.length);
n1.setTranslateX(x1);
n2.setTranslateX(x2);
barChart.getData().get(j-1).getData().get(0).setNode(n2);
barChart.getData().get(j).getData().get(0).setNode(n1);
temp = array_tmp[j - 1];
array_tmp[j - 1] = array_tmp[j];
array_tmp[j] = temp;
}
}
}
}
}
公共类排序扩展任务{
//这里有些东西
@凌驾
受保护的Void调用()引发异常{
taskThread=新线程(计数器,“时间”);
setDaemon(true);
taskThread.start();
int n=数组长度;
内部温度;
对于(int i=0;iarray_tmp[j]){
//更换钢筋
节点n1=条形图.getData().get(j-1).getData().get(0).getNode();
节点n2=条形图.getData().get(j).getData().get(0).getNode();
double x1=n1.getTranslateX()+((barChart.getWidth()-69)/array_tmp.length);
double x2=n2.getTranslateX()-((barChart.getWidth()-69)/array_tmp.length);
n1.setTranslateX(x1);
n2.setTranslateX(x2);
柱状图.getData().get(j-1).getData().get(0).setNode(n2);
barChart.getData().get(j.getData().get(0.setNode)(n1);
temp=数组_tmp[j-1];
数组_tmp[j-1]=数组_tmp[j];
数组_tmp[j]=温度;
}
}
}
}
}
JavaFX中的线程有两个基本规则:
阶段
)也受此规则约束private <T> Animation createSwapAnimation(Data<?, T> first, Data<?, T> second) {
double firstX = first.getNode().getParent().localToScene(first.getNode().getBoundsInParent()).getMinX();
double secondX = first.getNode().getParent().localToScene(second.getNode().getBoundsInParent()).getMinX();
double firstStartTranslate = first.getNode().getTranslateX();
double secondStartTranslate = second.getNode().getTranslateX();
TranslateTransition firstTranslate = new TranslateTransition(Duration.millis(500), first.getNode());
firstTranslate.setByX(secondX - firstX);
TranslateTransition secondTranslate = new TranslateTransition(Duration.millis(500), second.getNode());
secondTranslate.setByX(firstX - secondX);
ParallelTransition translate = new ParallelTransition(firstTranslate, secondTranslate);
translate.statusProperty().addListener((obs, oldStatus, newStatus) -> {
if (oldStatus == Animation.Status.RUNNING) {
T temp = first.getYValue();
first.setYValue(second.getYValue());
second.setYValue(temp);
first.getNode().setTranslateX(firstStartTranslate);
second.getNode().setTranslateX(secondStartTranslate);
}
});
return translate;
}
这是一个完整的演示。由于排序算法及其暂停被封装为任务
,因此如果需要,我们可以利用它的回调和状态属性。例如,我们在开始任务之前禁用按钮,并在任务完成时使用onSucceeded
处理程序再次启用它们。添加“取消”选项也很容易
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatedBubbleSort extends Application {
private Random rng = new Random();
private ExecutorService exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
});
@Override
public void start(Stage primaryStage) {
BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), new NumberAxis());
chart.setAnimated(false);
Series<String, Number> series = generateRandomIntegerSeries(10);
chart.getData().add(series);
Button sort = new Button("Sort");
Button reset = new Button("Reset");
reset.setOnAction(e -> chart.getData().set(0, generateRandomIntegerSeries(10)));
HBox buttons = new HBox(5, sort, reset);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(new Insets(5));
sort.setOnAction(e -> {
Task<Void> animateSortTask = createSortingTask(chart.getData().get(0));
buttons.setDisable(true);
animateSortTask.setOnSucceeded(event -> buttons.setDisable(false));
exec.submit(animateSortTask);
});
BorderPane root = new BorderPane(chart);
root.setBottom(buttons);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Task<Void> createSortingTask(Series<String, Number> series) {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
ObservableList<Data<String, Number>> data = series.getData();
for (int i = data.size() - 1; i >= 0; i--) {
for (int j = 0 ; j < i; j++) {
Data<String, Number> first = data.get(j);
Data<String, Number> second = data.get(j + 1);
Platform.runLater(() -> {
first.getNode().setStyle("-fx-background-color: green ;");
second.getNode().setStyle("-fx-background-color: green ;");
});
Thread.sleep(500);
if (first.getYValue().doubleValue() > second.getYValue().doubleValue()) {
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
Animation swap = createSwapAnimation(first, second);
swap.setOnFinished(e -> latch.countDown());
swap.play();
});
latch.await();
}
Thread.sleep(500);
Platform.runLater(() -> {
first.getNode().setStyle("");
second.getNode().setStyle("");
});
}
}
return null;
}
};
}
private <T> Animation createSwapAnimation(Data<?, T> first, Data<?, T> second) {
double firstX = first.getNode().getParent().localToScene(first.getNode().getBoundsInParent()).getMinX();
double secondX = first.getNode().getParent().localToScene(second.getNode().getBoundsInParent()).getMinX();
double firstStartTranslate = first.getNode().getTranslateX();
double secondStartTranslate = second.getNode().getTranslateX();
TranslateTransition firstTranslate = new TranslateTransition(Duration.millis(500), first.getNode());
firstTranslate.setByX(secondX - firstX);
TranslateTransition secondTranslate = new TranslateTransition(Duration.millis(500), second.getNode());
secondTranslate.setByX(firstX - secondX);
ParallelTransition translate = new ParallelTransition(firstTranslate, secondTranslate);
translate.statusProperty().addListener((obs, oldStatus, newStatus) -> {
if (oldStatus == Animation.Status.RUNNING) {
T temp = first.getYValue();
first.setYValue(second.getYValue());
second.setYValue(temp);
first.getNode().setTranslateX(firstStartTranslate);
second.getNode().setTranslateX(secondStartTranslate);
}
});
return translate;
}
private Series<String, Number> generateRandomIntegerSeries(int n) {
Series<String, Number> series = new Series<>();
for (int i = 1; i <= n; i++) {
series.getData().add(new Data<>(Integer.toString(i), rng.nextInt(90) + 10));
}
return series;
}
public static void main(String[] args) {
launch(args);
}
}
import java.util.Random;
导入java.util.concurrent.CountDownLatch;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入javafx.animation.animation;
导入javafx.animation.ParallelTransition;
导入javafx.animation.TranslateTransition;
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.collections.ObservableList;
导入javafx.concurrent.Task;
导入javafx.geometry.Insets;
导入javafx.geometry.Pos;
导入javafx.scene.scene;
导入javafx.scene.chart.BarChart;
导入javafx.scene.chart.CategoryAxis;
导入javafx.scene.chart.NumberAxis;
导入javafx.scene.chart.XYChart.Data;
导入javafx.scene.chart.XYChart.Series;
导入javafx.scene.control.Button;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.HBox;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类AnimatedBubbleSort扩展了应用程序{
私有随机rng=新随机();
private ExecutorService exec=Executors.newCachedThreadPool(可运行->{
螺纹t=新螺纹(可运行);
t、 setDaemon(true);
返回t;
});
@凌驾
公共无效开始(阶段primaryStage){
条形图=新条形图(新类别轴(),新编号轴());
图表.设置动画(假);
系列=发电机主导系列(10);
chart.getData().add(系列);
按钮排序=新按钮(“排序”);
按钮重置=新按钮(“重置”);
reset.setOnAction(e->chart.getData().set(0,GeneratorDomainIntegerSeries(10));
HBox按钮=新HBox(5,排序,重置);
按钮。设置对齐(位置中心);
按钮。设置填充(新插图(5));
排序设置操作(e->{
任务animateSortTask=createSortingTask(chart.getData().get(0));
按钮。设置禁用(true);
animateSortTask.setOnSucceeded(事件->按钮.setDisable(false));
执行提交(animateSortTask);
});
BorderPane根=新的BorderPane(图表);
root.setBottom(按钮);
场景=新场景(根);
初级阶段。场景(场景);
primaryStage.show();
}
专用任务createSortingTask(系列){
返回新任务(){
@凌驾
受保护的Void调用()引发异常{
ObservableList data=series.getData();
对于(int i=data.size()-1;i>=0;i--){
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatedBubbleSort extends Application {
private Random rng = new Random();
private ExecutorService exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
});
@Override
public void start(Stage primaryStage) {
BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), new NumberAxis());
chart.setAnimated(false);
Series<String, Number> series = generateRandomIntegerSeries(10);
chart.getData().add(series);
Button sort = new Button("Sort");
Button reset = new Button("Reset");
reset.setOnAction(e -> chart.getData().set(0, generateRandomIntegerSeries(10)));
HBox buttons = new HBox(5, sort, reset);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(new Insets(5));
sort.setOnAction(e -> {
Task<Void> animateSortTask = createSortingTask(chart.getData().get(0));
buttons.setDisable(true);
animateSortTask.setOnSucceeded(event -> buttons.setDisable(false));
exec.submit(animateSortTask);
});
BorderPane root = new BorderPane(chart);
root.setBottom(buttons);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Task<Void> createSortingTask(Series<String, Number> series) {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
ObservableList<Data<String, Number>> data = series.getData();
for (int i = data.size() - 1; i >= 0; i--) {
for (int j = 0 ; j < i; j++) {
Data<String, Number> first = data.get(j);
Data<String, Number> second = data.get(j + 1);
Platform.runLater(() -> {
first.getNode().setStyle("-fx-background-color: green ;");
second.getNode().setStyle("-fx-background-color: green ;");
});
Thread.sleep(500);
if (first.getYValue().doubleValue() > second.getYValue().doubleValue()) {
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
Animation swap = createSwapAnimation(first, second);
swap.setOnFinished(e -> latch.countDown());
swap.play();
});
latch.await();
}
Thread.sleep(500);
Platform.runLater(() -> {
first.getNode().setStyle("");
second.getNode().setStyle("");
});
}
}
return null;
}
};
}
private <T> Animation createSwapAnimation(Data<?, T> first, Data<?, T> second) {
double firstX = first.getNode().getParent().localToScene(first.getNode().getBoundsInParent()).getMinX();
double secondX = first.getNode().getParent().localToScene(second.getNode().getBoundsInParent()).getMinX();
double firstStartTranslate = first.getNode().getTranslateX();
double secondStartTranslate = second.getNode().getTranslateX();
TranslateTransition firstTranslate = new TranslateTransition(Duration.millis(500), first.getNode());
firstTranslate.setByX(secondX - firstX);
TranslateTransition secondTranslate = new TranslateTransition(Duration.millis(500), second.getNode());
secondTranslate.setByX(firstX - secondX);
ParallelTransition translate = new ParallelTransition(firstTranslate, secondTranslate);
translate.statusProperty().addListener((obs, oldStatus, newStatus) -> {
if (oldStatus == Animation.Status.RUNNING) {
T temp = first.getYValue();
first.setYValue(second.getYValue());
second.setYValue(temp);
first.getNode().setTranslateX(firstStartTranslate);
second.getNode().setTranslateX(secondStartTranslate);
}
});
return translate;
}
private Series<String, Number> generateRandomIntegerSeries(int n) {
Series<String, Number> series = new Series<>();
for (int i = 1; i <= n; i++) {
series.getData().add(new Data<>(Integer.toString(i), rng.nextInt(90) + 10));
}
return series;
}
public static void main(String[] args) {
launch(args);
}
}