我正在制作一个读取文本文件的程序。我想要做的是显示在文件读取之前或期间在单独的线程中创建的任意节点(警报或其他节点)。我尝试使用Task
和Platform.runLater()
这样:
if (filetoopen != null)
{
Platform.runLater(new Runnable() {
@Override
void run() {
Alert alert=new Alert(Alert.AlertType.INFORMATION)
alert.setHeaderText('TEST')
}
})
//method to read the file
Tools.convertFromFile(filetoopen,newredactor)
lastDirectory = filetoopen.getParentFile()
}
我想显示读取文件的警报或进度条,但是在读取完成后控件会初始化。那么,是否可以在读取文件时显示带有进度条的节点?或者我创建的Runnable将始终执行到底?
编辑:尝试使用任务:
class Alerter extends Task{
Alerter(File f,Editor e)
{
file=f
editor=e
}
File file
Editor editor
@Override
protected Object call() throws Exception {
Dialog dialog=new Dialog()
DialogPane dp=dialog.getDialogPane()
dp.setHeaderText('TEST')
dp.getButtonTypes().add(new ButtonType('Cancel',ButtonBar.ButtonData.CANCEL_CLOSE))
dialog.setOnCloseRequest(new javafx.event.EventHandler<DialogEvent>() {
@Override
void handle(DialogEvent event) {
dialog.close()
}
})
dialog.show()
Tools.convertFromFile(file,editor)
return null
}
}
对话框仍在Tools.convertFromFile
之后初始化。
答案 0 :(得分:1)
JavaFX中有两个线程规则(几乎所有其他UI工具包中都有):
您的第一个代码块违反了第二个规则(可能,您没有显示太多上下文),而您的第二个代码块违反了第一个规则。
所以基本上你需要:
您可以使用Platform.runLater(...)
来安排代码在FX应用程序线程上运行,但Task
class为这些更新提供了更方便的API。
所以:
// set up and show dialog:
ProgressBar progressBar = new ProgressBar();
DialogPane dialogPane = new DialogPane();
dialogPane.getButtonTypes().setAll(ButtonType.OK);
dialogPane.setHeaderText("Processing file");
dialogPane.setContent(progressBar);
dialogPane.lookupButton(ButtonType.OK).setDisable(true);
Dialog dialog = new Dialog();
dialog.setDialogPane(dialogPane);
dialog.show();
// create task:
Task<Void> task = new Task<Void>() {
@Override
public Void call() throws Exception {
Tools.convertFromFile(file, editor);
// can call updateProgress(...) here to update the progress periodically
return null ;
}
};
// update progress bar with progress from task:
progressBar.progressProperty().bind(task.progressProperty());
// when task completes, update dialog:
task.setOnSucceeded(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(1);
dialogPane.setHeaderText("Processing complete");
});
// handles errors:
task.setOnFailed(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(0);
dialogPane.setHeaderText("An error occurred");
});
// run task in background thread:
Thread thread = new Thread(task);
thread.start();
请注意,您的Tools.convertFromFile(...)
方法是从后台线程调用的,因此不得更新UI (或者至少该方法中任何更新UI的调用必须是包裹在Platform.runLater(...)
)。
这是一个完整的SSCCE(只是作为长时间运行过程的演示而睡觉):
import java.util.Random;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TaskWithProgressDemo extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Start process");
button.setOnAction(e -> {
button.setDisable(true);
// set up and show dialog:
ProgressBar progressBar = new ProgressBar();
DialogPane dialogPane = new DialogPane();
dialogPane.getButtonTypes().setAll(ButtonType.OK);
dialogPane.setHeaderText("Processing file in progress");
dialogPane.setContent(progressBar);
dialogPane.lookupButton(ButtonType.OK).setDisable(true);
Dialog<Void> dialog = new Dialog<Void>();
dialog.setDialogPane(dialogPane);
dialog.show();
// create task:
Task<Void> task = new Task<Void>() {
@Override
public Void call() throws Exception {
Random rng = new Random();
for (int i = 0 ; i <= 100 ; i++) {
Thread.sleep(rng.nextInt(40));
updateProgress(i, 100);
}
if (rng.nextBoolean()) {
System.out.println("Simulated error");
throw new Exception("An unknown error occurred");
}
return null ;
}
};
// update progress bar with progress from task:
progressBar.progressProperty().bind(task.progressProperty());
// when task completes, update dialog:
task.setOnSucceeded(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
button.setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(1);
dialogPane.setHeaderText("Processing complete");
});
// handles errors:
task.setOnFailed(event -> {
dialogPane.lookupButton(ButtonType.OK).setDisable(false);
button.setDisable(false);
progressBar.progressProperty().unbind();
progressBar.setProgress(0);
dialogPane.setHeaderText("An error occurred");
});
// run task in background thread:
Thread thread = new Thread(task);
thread.start();
});
StackPane root = new StackPane(button);
root.setPadding(new Insets(20));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
所以我终于明白了。我不得不将我的文件加载代码和进度更新移动到任务,因此它不会阻止FX线程。指示器显示加载文件的进度。
编辑:要在单独的非阻塞窗口中显示进度,必须使用新的Stage而不是其他任何东西。