Project: A Basic JavaFX App
Project: A Basic JavaFX App
This final lesson of the tutorial pulls together every concept covered so far — the Application lifecycle, the scene graph, layouts, controls, shapes, colors, fonts, event handling, and thread safety — into a single, working desktop application. Instead of isolated snippets, you will build a Unit Converter that converts kilometres to miles and Celsius to Fahrenheit, with a clean two-section UI, keyboard shortcuts, and a live result label that updates as the user types.
By the end you will have a complete, runnable program that demonstrates the patterns you will use in every real JavaFX project.
What the App Does
- Two conversion panels side by side inside a
HBox. - Each panel has a labelled
TextFieldfor input and a styled resultLabel. - Results update live as the user types, using a
ChangeListeneron the text property. - Invalid or empty input shows a polite "—" rather than crashing.
- A Reset button clears both fields; pressing Enter in either field triggers the same action.
- The primary
Stagehas a fixed minimum size and a custom title.
Project Structure
Everything lives in a single class to keep the focus on JavaFX. In a real project you would separate the UI-building code into a controller (or use FXML), but a single-file app is the right starting point for understanding the full wiring.
Add the JavaFX SDK modules to your Maven pom.xml (or module-path if running from the command line):
Full Source Code
Walking Through the Key Decisions
Helper Method for Panel Construction
The buildPanel() method accepts a Runnable so that pressing Enter in either field calls the matching conversion method. This avoids code duplication while keeping each panel's logic self-contained. The Runnable is a lambda: () -> convertKmToMiles(). Notice that the field and result Label are passed in as parameters so the calling code (in start()) retains references to them for live updates and for the reset action.
Live Updates via ChangeListener
Attaching a ChangeListener to field.textProperty() means the result recalculates on every keystroke. The listener receives three arguments — the observable, the old value, and the new value — but this code only cares about the side effect (calling the conversion), so all three can be ignored.
setOnAction for live updates? setOnAction on a TextField fires only when the user presses Enter. A ChangeListener on the text property fires on every character change — which is what gives the "live" feel. Both are wired here: Enter fires the same conversion for keyboard-workflow users.
Graceful Error Handling
The catch (NumberFormatException ex) block does not show an error dialog or print a stack trace. Instead it resets the result label to "—" and changes its colour to grey. This is the right UX for a live-updating field — the user is mid-typing and has not made an error yet; they just have an incomplete number.
Thread Safety
All the code in start() and in the event handlers runs on the JavaFX Application Thread. There are no background operations here, so no Platform.runLater() is needed. If you later extend this app to fetch an exchange rate from a web API, you would do the network call on a background thread and update the label inside Platform.runLater() — exactly as covered in the Threading lesson.
Inline CSS vs. a Stylesheet
The styling is done with setStyle() calls rather than an external CSS file. This is acceptable for a small standalone project and makes the code self-contained. For anything larger, prefer a styles.css loaded with scene.getStylesheets().add(...) — it separates concerns and enables theming.
setMinWidth / setMinHeight on the Stage to prevent the user from resizing the window so small that controls overlap. JavaFX will respect the minimum size during interactive resizing but will still let you set the initial size freely.
Extending the Project
Here are natural next steps that each introduce one new JavaFX concept:
- Add a
ComboBoxfor unit selection — introduces observable value bindings and theChoiceBox/ComboBoxcontrols. - Load an external stylesheet — moves style rules to
converter.css, teaches the JavaFX CSS system. - Add conversion history with a
ListView— introducesObservableListandFXCollections. - Fetch a live currency rate — forces you to use a background
ThreadorTaskandPlatform.runLater().
Summary
This project is small by design — every line serves a teaching purpose. You have seen how the Application entry point, the scene graph, layout containers, controls, properties, listeners, and event handlers all fit together into a working whole. The patterns you used here — building the scene in start(), wiring listeners to observable properties, separating conversion logic from UI wiring, and handling bad input gracefully — are the same patterns you will apply in applications of any size. From here, every new JavaFX topic (FXML, bindings, animations, custom controls) is an extension of this foundation.