JavaFX 提供了两个用于显示任务进度的核心组件:
ProgressIndicator
:以圆形动画的形式展示任务进度,适用于未定义进度的任务。ProgressBar
:以水平条的形式展示任务进度,适用于可以量化进度的任务。这两个组件可直接绑定到后台任务的 progress
属性,实时更新界面。
ProgressBar
是以 水平条 的形式显示任务进度,适用于可量化的任务。
ProgressBar()
ProgressBar(double progress)
progress
:范围为 [0.0, 1.0]
,表示百分比进度。方法名 | 说明 | 示例 |
---|---|---|
setProgress(double value) |
设置进度条的进度值。value 范围为 [0.0, 1.0] ,-1 表示不确定的进度(类似于加载动画)。 |
progressBar.setProgress(0.5); |
getProgress() |
获取当前进度条的进度值。 | double progress = progressBar.getProgress(); |
progressProperty() |
返回一个 DoubleProperty ,可以用于绑定任务的进度属性。 |
progressBar.progressProperty().bind(task.progressProperty()); |
方法名 | 说明 | 示例 |
---|---|---|
setStyle(String style) |
设置进度条的 CSS 样式。可以自定义进度条的外观。 | progressBar.setStyle("-fx-accent: green;"); |
ProgressBar progressBar = new ProgressBar();
progressBar.setProgress(0.5); // 设置进度为50%
progressBar.setStyle("-fx-accent: blue;"); // 将进度条颜色设置为蓝色
ProgressIndicator
以 圆形动画 的形式显示任务进度,适用于未定义具体进度的任务(如等待加载)。
ProgressIndicator()
ProgressIndicator(double progress)
方法名 | 说明 | 示例 |
---|---|---|
setProgress(double value) |
设置指示器的进度值,value 范围为 [0.0, 1.0] ,-1 表示不确定的进度状态(默认值)。 |
progressIndicator.setProgress(-1); |
getProgress() |
获取当前指示器的进度值。 | double progress = progressIndicator.getProgress(); |
progressProperty() |
返回一个 DoubleProperty ,可以用于绑定任务的进度属性。 |
progressIndicator.progressProperty().bind(task.progressProperty()); |
方法名 | 说明 | 示例 |
---|---|---|
setStyle(String style) |
设置指示器的 CSS 样式。可以自定义指示器的外观。 | progressIndicator.setStyle("-fx-accent: red;"); |
ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.setProgress(-1); // 设置为不确定状态
progressIndicator.setStyle("-fx-accent: green;"); // 设置指示器颜色为绿色
功能 | ProgressBar |
ProgressIndicator |
---|---|---|
表示形式 | 水平条形进度 | 圆形指示器 |
初始进度 | 默认 0.0 | 默认 -1(不确定进度) |
绑定支持 | 支持绑定任务的 progressProperty() |
支持绑定任务的 progressProperty() |
适用场景 | 可量化的任务进度 | 未量化的任务(如加载等待) |
进度状态 | 进度条会逐步增长 | 圆形动画逐步覆盖 |
问题原因:
在 JavaFX 中,所有 UI 更新都运行在单线程模式下。如果后台任务运行在 UI 线程,会导致界面卡顿。
解决方案:
Task
将后台任务与 UI 线程分离。Task
的 call
方法中执行。问题原因:
任务更新过于频繁,导致 UI 线程处理不过来。
解决方案:
if (i % (NUM_TASKS / 10) == 0) {
updateProgress(i, NUM_TASKS);
}
问题原因:
任务执行时可能抛出异常,导致应用崩溃。
解决方案:
setOnFailed
方法中处理异常:task.setOnFailed(event -> {
Throwable exception = task.getException();
System.err.println("Task failed: " + exception.getMessage());
});
问题原因:
用户可能需要取消任务,但未正确处理会导致资源泄漏。
解决方案:
Task
的 cancel
方法并处理中断:if (isCancelled()) {
updateMessage("Task cancelled.");
break;
}
确保 JavaFX 的依赖正确引入(适配 JavaFX 21 和 JDK 8):
org.openjfx
javafx-controls
21
org.openjfx
javafx-base
21
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ReloadableLoadingExample extends Application {
private final ExecutorService threadPool = Executors.newFixedThreadPool(5);
@Override
public void start(Stage primaryStage) {
// 主界面组件
ProgressBar progressBar = new ProgressBar(0);
ProgressIndicator progressIndicator = new ProgressIndicator(0);
Button startButton = new Button("Start Loading");
Label statusLabel = new Label("Click 'Start Loading' to begin.");
VBox content = new VBox(10, startButton, statusLabel, progressBar, progressIndicator);
content.setStyle("-fx-padding: 20; -fx-alignment: center;");
// 隐藏进度条和指示器
progressBar.setVisible(false);
progressIndicator.setVisible(false);
// 遮罩层
StackPane overlay = createOverlay(content);
overlay.setVisible(false); // 默认隐藏遮罩层
// 主界面
StackPane root = new StackPane(content, overlay);
Scene scene = new Scene(root, 400, 250);
// 启动任务按钮
startButton.setOnAction(event -> {
// 禁用按钮,显示遮罩和进度条
startButton.setDisable(true);
overlay.setVisible(true);
progressBar.setVisible(true);
progressIndicator.setVisible(true);
// 创建任务
Task task = createLoadingTask(statusLabel);
// 重置进度和状态
progressBar.progressProperty().unbind();
progressIndicator.progressProperty().unbind();
progressBar.setProgress(0);
progressIndicator.setProgress(0);
// 绑定任务进度
progressBar.progressProperty().bind(task.progressProperty());
progressIndicator.progressProperty().bind(task.progressProperty());
// 提交任务到线程池
threadPool.submit(task);
// 任务完成时的处理
task.setOnSucceeded(e -> {
overlay.setVisible(false);
startButton.setDisable(false);
progressBar.setVisible(false);
progressIndicator.setVisible(false);
showAlert(Alert.AlertType.INFORMATION, "Task Completed", "The task completed successfully!");
});
// 任务失败时的处理
task.setOnFailed(e -> {
overlay.setVisible(false);
startButton.setDisable(false);
progressBar.setVisible(false);
progressIndicator.setVisible(false);
showAlert(Alert.AlertType.ERROR, "Task Failed", "An error occurred: " + task.getException().getMessage());
});
// 任务取消时的处理
task.setOnCancelled(e -> {
overlay.setVisible(false);
startButton.setDisable(false);
progressBar.setVisible(false);
progressIndicator.setVisible(false);
showAlert(Alert.AlertType.WARNING, "Task Cancelled", "The task was cancelled.");
});
});
primaryStage.setScene(scene);
primaryStage.setTitle("Reloadable JavaFX Loading Example");
primaryStage.show();
}
/**
* 创建任务
*/
private Task createLoadingTask(Label statusLabel) {
return new Task() {
private static final int TOTAL_WORK = 100;
@Override
protected Void call() throws Exception {
for (int i = 1; i <= TOTAL_WORK; i++) {
if (isCancelled()) {
updateMessage("Task cancelled.");
break;
}
// 模拟任务执行
Thread.sleep(50);
// 更新进度和状态
updateProgress(i, TOTAL_WORK);
updateMessage("Progress: " + i + "/" + TOTAL_WORK);
}
if (!isCancelled()) {
updateMessage("Task completed successfully!");
}
return null;
}
@Override
protected void updateMessage(String message) {
// 界面线程中更新状态
javafx.application.Platform.runLater(() -> statusLabel.setText(message));
}
};
}
/**
* 创建遮罩层
*/
private StackPane createOverlay(Region parent) {
Rectangle overlayRectangle = new Rectangle();
overlayRectangle.setFill(Color.rgb(0, 0, 0, 0.3));
overlayRectangle.widthProperty().bind(parent.widthProperty());
overlayRectangle.heightProperty().bind(parent.heightProperty());
Label loadingLabel = new Label("Loading...");
loadingLabel.setStyle("-fx-font-size: 16px; -fx-text-fill: white;");
StackPane overlay = new StackPane(overlayRectangle, loadingLabel);
overlay.setAlignment(Pos.CENTER);
return overlay;
}
/**
* 显示提示信息
*/
private void showAlert(Alert.AlertType alertType, String title, String content) {
Alert alert = new Alert(alertType);
alert.setTitle(title);
alert.setContentText(content);
alert.showAndWait();
}
@Override
public void stop() {
threadPool.shutdownNow();
}
public static void main(String[] args) {
launch(args);
}
}