SampleFX – Updating a Label

The humble label is probably one of the most used widgets in the UI designers toolkit so getting to know it inside and out is important. A common task it is to update a label to display some new information depending on the state of the application. Depending on where that new information comes from it can be trivial to update the label or require an understanding of threading and thread safety.

If the new information to be displayed on the label comes from the main application thread (the JavaFX thread) then it can be set straight into the label, this is safe because the scene graph the label is in is owned by the JavaFX thread. If the new information is coming from the another thread however care must be taken to ensure that the update is performed on the JavaFX thread.

One way to update the UI from another thread is to use a call to Platform.runLater passing in a Runnable. Anyone who has used Swing will be familiar with this method as it was used extensively there. A typical invocation that updates a label that displays the current time would look something like this.

dynamicTimeDisplayLabel.textProperty().bind(dynamicTimeProperty);
Thread t = new Thread(new Runnable() {
	@Override
	public void run() {
		while (true) {
			final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
			Platform.runLater(new Runnable() {
				@Override
				public void run() {
					dynamicTimeProperty.set(sdf.format(new Date()));
				}
			});
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
				break;
			}
		}
	}
});
t.setName("Runnable Time Updater");
t.setDaemon(true);
t.start();

The label has its text property bound to a SimpleStringProperty. The application then spawns a new thread which simply formats the current time and sets the value of the property. The setting of the property must occur on the JavaFX thread hence the use of Platform.runLater. Note: As I’m writing this article I’ve noticed that there’s two improvements I could make. As shown the formatting of the date string will take place on the JavaFX thread but there’s no need for that it could just as easily happen on the worker thread as long as the result was assigned to a final String variable. Additionally the simple date format is inside the while loop and so created fresh each time, this is unnecessary since trivial to ensure it can only be accessed from one thread at a time. An enhanced version of this code is shown below.

dynamicTimeDisplayLabel.textProperty().bind(dynamicTimeProperty);
Thread t = new Thread(new Runnable() {
	@Override
	public void run() {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		while (true) {
			final String date = sdf.format(new Date());
			Platform.runLater(new Runnable() {
				@Override
				public void run() {
					dynamicTimeProperty.set(date);
				}
			});
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
				break;
			}
		}
	}
});
t.setName("Runnable Time Updater");
t.setDaemon(true);
t.start();

While the method shown above certainly works JavaFX 2 provides a more developer friendly way of achieving the same result. Rather than creating a Runnable you create a Task which comes complete with a couple of thread safe properties that can be used to update the UI with the minimum of fuss. An example of using a task is shown below.

Task dynamicTimeTask = new Task<Void>() {
	@Override
	protected Void call() throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		while (true) {
			updateMessage(sdf.format(new Date()));
			try {
				Thread.sleep(100);
			} catch (InterruptedException ex) {
				break;
			}
		}
		return null;
	}
};
dynamicTimeDisplayLabel2.textProperty().bind(dynamicTimeTask.messageProperty());
Thread t2 = new Thread(dynamicTimeTask);
t2.setName("Tesk Time Updater");
t2.setDaemon(true);
t2.start();

As you can see this is similar to the first example just without the less than beautiful anonymous inner Runnable. Since Task defines a thread safe string property called message it’s simply a  matter of binding the text property of the label to the message property of the task. The call to updateMessage in the task is all that is required to update the property.

While this is certainly a safe and correct way to achieve this type of functionality I actually slightly prefer the first example. To my mind Task is there for background processing that has a lifetime shorter than the application and typically returns a result. For example a Task might be used to compress a file or perform an image transformation. The example shown here uses a task that (probably) doesn’t end until the application ends.

As always the project can be downloaded from here.