我注意到 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();
}
}
}
最佳答案
是的,你是对的,Platform.runLater()
(如名称所暗示)不会立即运行,只是将Runnable
推送到内部队列。深处有一个内部渲染标记。 Runnable
对象将在渲染之前的更新标记中执行。标签从不显示“1”的事实与您的 runNow()
在同一时间点内立即被调用的事实恰好吻合,因此两个 Runnable
对象被推送到同一队列并在 JavaFX 内部循环中的同一时间点中执行。因此,会发生以下情况:
- 标签设置为 1
- 内部线程设置为 sleep 状态。如果您注意到的话,这实际上会卡住应用程序,因为渲染线程现在正在休眠
- 标签设置为 2
- 发生渲染标记,因此我们可以看到“2”
- ...
我尝试运行上面的代码,有时我可以看到 1,这意味着两个 Runnable
对象被推送到不同的刻度中。类似这样的事情:
- 标签设置为 1
- 渲染勾选
- ...
关于javafx - JavaFX 线程何时绘制到显示器上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31544427/