When it comes to creating sleek and responsive user interfaces in Java, JavaFX is a game-changer. Among its many offerings, the ProgressIndicator control stands out as a key component for presenting progress and loading feedback in your applications. In this article, we will explore the JavaFX’s ProgressIndicator and how it can be used to enhance the user experience in your Java applications.
What is a ProgressIndicator?
The ProgressIndicator is a visual component in JavaFX designed to display the progress of a lengthy task or operation. It provides users with a clear indication that something is happening, preventing frustration and uncertainty during potentially time-consuming operations. This control is a valuable addition to applications that require any form of data retrieval, processing, or other time-intensive actions.
Creating a Simple ProgressIndicator
Let’s start by creating a basic ProgressIndicator in JavaFX. This example demonstrates how to create a simple progress indicator that spins indefinitely to indicate an ongoing process.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressIndicatorBasic extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("ProgressIndicator Basic");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
}
}
In this example, we create a simple JavaFX application with a ProgressIndicator. When you run the application, you will see a spinning wheel in the center of the window, indicating that some task is in progress. However, this is just the beginning; there’s a lot more you can do with the ProgressIndicator.
Setting ProgressIndicator Progress
To display a specific level of progress, you can set the ProgressIndicator’s progress property, which is a double value between 0.0 and 1.0:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class DeterminateProgressIndicator extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("Determinate ProgressIndicator");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
// Set the progress to 50 percent
progressIndicator.setProgress(0.5);
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
}
}
This will display the progress indicator at the halfway point, 50 percent.
Indeterminate vs. Determinate ProgressIndicator
The ProgressIndicator can represent both determinate and indeterminate progress. Determinate progress indicates a specific percentage of completion, whereas indeterminate progress simply signifies that a task is ongoing without specifying the exact progress percentage. Indeterminate mode is useful when you don’t know the exact duration of a task or when you want to show that something is happening without providing a progress percentage.
To set a ProgressIndicator to determinate mode, specify a non-negative progress using the setProgress() method, as shown in the previous section. To make it indeterminate, which you will not need to do, considering the default is -1.0 when you don’t set the progress. However, if you want to do it manually, set the progress to a negative value:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class IndeterminateProgressIndicator extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("Indeterminate ProgressIndicator");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.setProgress(-1.0);
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
}
}
The indeterminate ProgressIndicator will continuously spin to indicate an ongoing process.
Changing ProgressIndicator Size
You can change the size of the ProgressIndicator by setting its dimensions using the setPrefSize(double width, double height), the setMinSize(double width, double height), or the setMaxSize(double width, double height) methods. This is useful when you need a smaller or larger spinner to fit your layout.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressIndicatorSize extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("ProgressIndicator Size");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
// Set the progress to 50 percent
progressIndicator.setProgress(0.7);
progressIndicator.setMinSize(150, 150);
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
}
}
This code sets the width and height of the ProgressIndicator to 150 pixels each.
Changing ProgressIndicator Style
JavaFX also allows you to apply CSS styles to the ProgressIndicator, giving you control over its appearance. For instance, you can change the spinner’s color:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class IndeterminateProgressIndicator extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("Indeterminate ProgressIndicator");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.setProgress(-1.0);
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
}
}
This code changes the spinner’s color to red. You can further customize its appearance by defining your own CSS styles.
Binding ProgressIndicator to a Task
A typical use case for a ProgressIndicator is to associate it with a task or operation to reflect the actual progress. JavaFX provides a seamless way to bind a ProgressIndicator to a Task or a Service using the Bindings class:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressIndicatorTaskBinder extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("ProgressIndicator Task Binder");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create Counter Task
Counter counter = new Counter();
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
// Bind the progress indicator progress to the counter task progress
progressIndicator.progressProperty().bind(counter.progressProperty());
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
// Start the task
Thread thread = new Thread(counter);
thread.start();
}
}
class Counter extends Task<Void> {
@Override
protected Void call() throws Exception {
// Simulate a time-consuming task
for (int i = 1; i <= 100; i++) {
updateProgress(i, 100); // Update progress
Thread.sleep(100);
}
return null;
}
}
In this example, we create a simple Task that simulates a time-consuming operation. We then bind the ProgressIndicator to the task’s progress property using bind(). As the task progresses, the ProgressIndicator will automatically reflect the task’s progress.
Handling Task Completion
To handle tasks’ completion, you can add listeners to the Worker.State property of the task. This allows you to perform actions when the task finishes or encounters an error:
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ProgressIndicatorTaskStatus extends Application {
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private final StackPane parent = new StackPane();
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(this.parent, WIDTH, HEIGHT);
// Sets the stage title
stage.setTitle("ProgressIndicator Task Status");
// Set the stage scene
stage.setScene(scene);
// Center stage on screen
stage.centerOnScreen();
// Show stage on screen
stage.show();
}
@Override
public void init() throws Exception {
super.init();
// Create Counter Task, and add a state listener
Counter counter = new Counter();
counter.stateProperty().addListener(this::counterState);
// Create the ProgressIndicator
ProgressIndicator progressIndicator = new ProgressIndicator();
// Bind the progress indicator progress to the counter task progress
progressIndicator.progressProperty().bind(counter.progressProperty());
// Add the ProgressIndicator to the parent StackPane
this.parent.getChildren().add(progressIndicator);
// Start the task
Thread thread = new Thread(counter);
thread.start();
}
private void counterState(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
System.out.println("Task completed successfully.");
} else if (newState == Worker.State.FAILED) {
System.out.println("Task encountered an error.");
}
}
}
class Counter extends Task<Void> {
@Override
protected Void call() throws Exception {
// Simulate a time-consuming task
for (int i = 1; i <= 100; i++) {
updateProgress(i, 100); // Update progress
Thread.sleep(100);
}
return null;
}
}
By monitoring the task’s state, you can update the user interface accordingly, e.g., showing success or error messages.
Conclusion
JavaFX’s ProgressIndicator is a valuable tool for enhancing the user experience in your Java applications. Whether you’re developing a file upload feature, fetching data from a server, or handling batch processing, the ProgressIndicator control can help you provide informative feedback to your users.
Incorporate the JavaFX ProgressIndicator into your projects and elevate your application’s user interface, providing your users with a seamless, interactive, and visually pleasing experience.
I hope you found this article informative and useful. If you would like to receive more content, please consider subscribing to our newsletter.