Multithreading JavaFX制作条形图在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 =

我得到了JavaFX主线程,在那里我创建了新的线程来扩展任务、排序和替换条。一切都很好,但我想在替换时做一些延迟(比如100ms),以显示一步一步的排序,可能是动画。问题是,当我使用Thread.sleep()或TranslateTransition()时,它只是将所有延迟(毫秒)加在一起,形成一个大延迟,这个延迟发生在更改条之前。如何使延迟在UI线程中正常工作

在主要课堂上:

       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中的线程有两个基本规则:

  • 作为实际显示的场景图一部分的UI组件(节点)只能从JavaFX应用程序线程访问。其他一些操作(如创建新的
    阶段
    )也受此规则约束
  • 任何长时间运行或阻塞操作都应该在后台线程(即,不是JavaFX应用程序线程)上运行。这是因为JavaFX应用程序线程是呈现UI和响应用户交互所需的线程。因此,如果阻止FX应用程序线程,则在操作完成之前,UI无法呈现,应用程序将变得无响应
  • 提供了管理可在后台线程上运行的代码和在FX应用程序线程上执行回调的工具

    另外,还提供了允许在特定时间在JavaFX应用程序线程上执行UI代码的类。请注意,动画API完全避免创建背景线程

    因此,对于您的用例,如果您想在条形图中设置两个条形图的交换动画,可以使用动画API来实现。创建执行此类交换的动画的常规方法可能如下所示:

    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);
        }
    }