【问题标题】:When does the JavaFX Thread paint to the display?JavaFX Thread 何时绘制到显示器上?
【发布时间】:2015-07-21 16:17:14
【问题描述】:

我注意到 Platform.runLater() 在运行后不会立即更新舞台/屏幕,所以我猜这幅画正在其他地方或另一个排队事件中发生。在可运行文件完成后,我很好奇何时或如何将实际的绘画或渲染到屏幕上排队或发出信号。

以下代码将在控制台打印1.begin, 1.end, 2.begin, 2.end, 3.begin, 3.end,但标签从不显示1,尽管第二个runLater() 等待。

package main;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.concurrent.CountDownLatch;

public class SmallRunLater extends Application {

    SimpleStringProperty labelValue = new SimpleStringProperty("0");

    @Override
    public void start(Stage stage) throws InterruptedException {

        Scene scene = new Scene(new Group());
        stage.setWidth(550);
        stage.setHeight(550);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));

        Label label = new Label();
        label.textProperty().bind(labelValue);
        Button startBtn = new Button("Start");
        vbox.getChildren().addAll(startBtn, label);

        startBtn.setOnAction((action) -> {
            try {
                Task task = new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        SmallWork work = new SmallWork();
                        work.doWork(labelValue);
                        return null;
                    }
                };
                new Thread(task).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

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

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

}

class SmallWork {

    public void doWork(SimpleStringProperty labelValue) throws InterruptedException {

        Platform.runLater(() -> {
            System.out.println("1.begin");
            labelValue.set("1");
            System.out.println("1.end");
        });

        runNow(() -> {
            System.out.println("2.begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            labelValue.set("2");
            System.out.println("2.end");
        });

        Platform.runLater(() -> {
            System.out.println("3.begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            labelValue.set("3");
            System.out.println("3.end");

        });
    }

    public static void runNow(Runnable r){

        final CountDownLatch doneLatch = new CountDownLatch(1);

        Platform.runLater(() -> {
            try {
                r.run();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                doneLatch.countDown();
            }
        });

        try {
            doneLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【问题讨论】:

标签: javafx java-8


【解决方案1】:

是的,你是对的,Platform.runLater()(顾名思义)不会立即运行,只是将Runnable 推送到内部队列。深处有一个内部渲染刻度。 Runnable 对象将在渲染前的更新滴答中执行。标签永远不会显示“1”的事实恰好与您的 runNow() 在同一个滴答中立即被调用的事实相吻合,因此两个 Runnable 对象被推送到同一个队列并在 JavaFX 中的同一个滴答中执行内部循环。因此,会发生以下情况:

  1. 标签设置为 1
  2. 内部线程设置为睡眠。如果您注意到,这实际上会冻结应用程序,因为渲染线程现在正在休眠
  3. 标签设置为 2
  4. 渲染滴答发生,所以我们看到“2”
  5. ...

我已经尝试运行上面的代码,有时我可以看到 1,这意味着两个 Runnable 对象被推送到不同的滴答声中。类似的东西:

  1. 标签设置为 1
  2. 渲染刻度
  3. ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-10
    • 2019-01-15
    • 1970-01-01
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    相关资源
    最近更新 更多