我已经阅读了类似的问题但是当我向VBox
添加许多节点时,我的UI仍然处于冻结状态。我在下面提供了一个功能齐全的程序,可以清楚地说明问题。
4秒后,ProgressIndicator
冻结,因为5000个节点被添加到VBox
。这是用于演示JavaFX线程冻结的过多数量,尽管使用Task(用于非UI工作)然后使用Platform.runLater()
将节点添加到场景中。
在我的实际应用中,我没有添加空白TitlePane
,而是通过TitlePane
添加从FXML文件中获取的new FXMLLoader()
,然后初始化loader.load()
相关的控制器,它反过来初始化一些中等要求的计算 - 这些计算正在JavaFX线程上执行!因此,即使我正在添加更接近250个节点,但在最终使用Platform.runLater
时,UI仍然会冻结。如何让ProgressIndicator
冻结,直到显示红色背景?
完整示例:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Timer;
import java.util.TimerTask;
public class FreezingUI extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
VBox mainBox = new VBox();
mainBox.setPrefHeight(800);
mainBox.setStyle("-fx-background-color: #f1f1f1; -fx-alignment: center");
Label label = new Label();
label.setMinHeight(50);
label.setStyle("-fx-font-size: 24px; -fx-text-fill: #515151");
ProgressIndicator progressIndicator = new ProgressIndicator(ProgressIndicator.INDETERMINATE_PROGRESS);
mainBox.getChildren().addAll(progressIndicator, label);
Scene scene = new Scene(mainBox, 500, 800);
primaryStage.setScene(scene);
primaryStage.show();
Timer timer = new Timer();
TimerTask task = new TimerTask(){
private int i = 4;
public void run(){
if (i >= 0) {
Platform.runLater(()->{
label.setText("Freezing in " + i--);
});
}else{
addNodesToUI(mainBox);
timer.cancel();
}
}
};
timer.scheduleAtFixedRate(task, 0, 1000);
}
private void addNodesToUI(VBox mainBox) {
final int[] i = {0};
Platform.runLater(() -> {
Accordion temp = new Accordion();
mainBox.getChildren().add(temp);
while (i[0] < 5000) {
TitledPane tp = new TitledPane();
tp.setPrefWidth(300);
tp.setPrefHeight(12);
tp.setPadding(new Insets(10));
tp.setStyle("-fx-background-color: red;");
temp.getPanes().add(tp);
i[0]++;
}
});
}
}
答案 0 :(得分:2)
这种情况正在发生,因为您要求UI线程在一个大块中完成大量工作。在创建所有5000个节点并将其添加到场景之前,UI线程无法退出while
循环。
private void addNodesToUI(VBox mainBox) {
final int[] i = {0};
Accordion temp = new Accordion();
Platform.runLater(() -> {
mainBox.getChildren().add(temp);
});
while (i[0] < 5000) {
TitledPane tp = new TitledPane();
tp.setPrefWidth(300);
tp.setPrefHeight(12);
tp.setPadding(new Insets(10));
tp.setStyle("-fx-background-color: red;");
i[0]++;
Platform.runLater(() -> {
temp.getPanes().add(tp);
});
}
}
这将允许您小批量创建节点。这样,UI线程可以在逐步添加节点时尝试呈现UI。
对于您的FXML案例,您可以在另一个线程中创建和加载FXML。将场景分支附加到场景中时,您只需要处于UI线程中。但是,我怀疑这只会减轻影响,因为你仍然会一次性附加一大块。