JavaFX使条形图通过UI线程中的延迟更改条形图 [英] JavaFX make bar chart changing bars with delay in UI Thread

查看:47
本文介绍了JavaFX使条形图通过UI线程中的延迟更改条形图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有JavaFX主线程,在这里创建了新线程,该线程扩展了Task并进行了排序和替换.一切都很好,但是我想在替换时进行一些延迟(例如100ms)以逐步显示排序,也许是动画.问题是,当我使用Thread.sleep()或TranslateTransition()时,它只是将所有延迟毫秒相加在一起,而这是在更改小节之前发生的一大延迟.如何使延迟在UI线程中正常工作?

I got JavaFX main thread, there I create new Thread that extends Task and sort and replace bars. Everyhing is good, but I want to make some delays(like 100ms) while replacing to show step by step sorting, maybe with animation. The problem is when I use Thread.sleep() or TranslateTransition() it just sum all delays miliseconds together in one big delay that happens before changing bars. How can I make delay that will work properly in UI thread?

在主班:

       Sorting sorting = new Sorting();
       sortThread = new Thread(sorting, "sort");
       sortThread.start();
       sortThread.join();

我的课程排序扩展了Task

And my class Sorting extends Task

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

推荐答案

JavaFX中有两个基本的线程规则:

There are two basic rules to threading in JavaFX:

    只能从JavaFX应用程序线程访问作为实际显示的场景图的一部分的
  1. UI组件(节点).其他一些操作(例如,创建新的Stage)也要遵守此规则.
  2. 任何长时间运行或阻塞的操作都应在后台线程(即不是JavaFX应用程序线程)上运行.这是因为JavaFX应用程序线程是呈现UI和响应用户交互所需的线程.因此,如果您阻止了FX Application Thread,则无法呈现UI,并且该应用程序将变得无响应,直到您的操作完成为止.
  1. UI components (nodes) that are part of a scene graph that is actually displayed can only be accessed from the JavaFX application thread. Some other operations (such as creating a new Stage) are also subject to this rule.
  2. Any long-running or blocking operation should be run on a background thread (i.e. not the JavaFX application thread). This is because the JavaFX application thread is the one needed to render the UI and respond to user interaction. Consequently, if you block the FX Application Thread, then the UI cannot be rendered and the application will become unresponsive until your operation completes.

javafx.concurrent API 提供了用于管理可在后台线程上运行的代码以及在FX应用程序线程上执行回调的工具.

The javafx.concurrent API provides facilities for managing code that can be run on background threads and executing callbacks on the FX application thread.

javafx.animation API 还提供了允许在特定时间在JavaFX应用程序线程上执行UI代码的类.请注意,动画API完全避免创建后台线程.

The javafx.animation API additionally provides classes that allow UI code to be executed on the JavaFX application thread at specific times. Note that the animation API avoids creating background threads at all.

因此,在您的用例中,如果要对条形图中的两个条形交换进行动画处理,可以使用animation API进行.创建执行这种交换的动画的通用方法可能如下所示:

So for your use case, if you want to animate the swapping of two bars in the bar chart, you can do so with the animation API. A general method that creates an animation that performs such a swap might look like this:

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

这里的基本思想很简单:我们在两个节点之间的x坐标中测量距离;记下它们当前的translateX属性,然后创建两个过渡来移动节点,使它们彼此占据位置.这两个转换是并行执行的.过渡完成后(由过渡状态从RUNNING变为其他状态来指示),图表中的值将交换并且translateX属性重置为以前的值(这些效果将在视觉上抵消) ,但现在图表数据将反映出两者已被交换的事实).

The basic idea here is pretty simple: we measure the distance in the x-coordinates between the two nodes; make a note of their current translateX properties, and then create two transitions which move the nodes so they take each others positions. Those two transitions are executed in parallel. When the transitions are complete (indicated by the status of the transition changing from RUNNING to something else), the values in the chart are exchanged and the translateX properties reset to their previous values (the effect of these will cancel out visually, but now the chart data will reflect the fact that the two have been exchanged).

如果您想执行一个排序算法,使排序中的交换动画化,在算法的每个步骤之间暂停,则可以使用后台线程来执行此操作(您也可以通过动画来执行此操作-但这可以似乎足够简单,也许更具指导性.)

If you want to perform a sort algorithm which animates the exchanges in the sorting, pausing between each step of the algorithm, you can do this using a background thread (you may be able to do this with an animation too - but this seems simple enough and is perhaps more instructional).

这里的想法是创建一个Task,其call()方法执行排序算法,在各个点处暂停以使用户可以查看正在发生的情况.因为我们正在暂停(阻塞),所以不能在FX Application Thread上运行它,因为阻塞会阻止UI更新,直到整个过程完成为止.

The idea here is to create a Task whose call() method performs the sort algorithm, pausing at various points to allow the use to see what is happening. Because we are pausing (blocking), this cannot be run on the FX Application Thread, as the blocking would prevent the UI being updated until the entire process was complete.

这里是冒泡排序的实现(为简单起见).在排序的每个迭代中,我们:

Here is an implementation of a bubble sort (for simplicity). On each iteration of the sort, we:

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆