JavaFX Fahrenheit to Celsius Converter in FXML

JavaFX Fahrenheit to Celsius Example
JavaFX Fahrenheit to Celsius example using FXML to layout the Scene on the Stage.

JavaFX is easy once you get the hang of it, but for new programmers that requires plenty of examples. More experienced programmers will find one or two examples are enough to master most of JavaFX, so a lot of web sites only put one or two examples up—enough for experienced programmers, but not nearly enough for newer programmers.

In this post, I present the classic temperature converter example modified to use JavaFX. This example uses basic MVC/MVP design. If you want to see this same JavaFX tutorial written in pure Java, look for my other tutorial on this site. It will be the one mentioning pure Java instead of FXML.

Creating a JavaFX Project in Eclipse

In Eclipse create a JavaFX project by selecting File->New->Project… from the menu bar. You will need e(fx)clipse installed in your Eclipse to create a JavaFX project.

JavaFX Project Creation
JavaFX Project created from the Select a Wizard dialog in Eclipse.

You can put minimal information in, and create the project. You can modify it by hand, later.

Select a JavaFX Project Name
You can stick to naming the project and making sure you have Java 8 selected. You can modify everything else later by hand.

Choose a name for your JavaFX project and select finished. You end up with a Main class, an application CSS file and some JavaFX libraries. Not too much, but we’ll add more to the project and make it work.

First thing’s first. You probably don’t have the package name you want. I suggest creating the package name you like and move the CSS and Java files into it. I used whatisjavafx.com for package name, but you can pick one you like. Just modify the code examples in this tutorial to match the package structure you want.

First, let’s swap out the code in Main.java with our own. Use the following code. Remember to change the package name if necessary.

package com.whatisjavafx;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
  @Override
  public void start(Stage stage)
      throws IOException {
    Parent root = 
        FXMLLoader
        .load(getClass()
            .getResource("convert.fxml"));
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.setTitle("Temperature Converter");
    stage.show();
  }

  public static void main(String[] args) {
    launch(args);
  }
}

 

The Main class extends javafx.application.Application as do all main classes for JavaFX projects. Obviously, you could rename your Main class to be any name you want, but it still needs to extend Application.

Be sure to call launch() in your main() method. The launch() method is what starts your JavaFX. It ends up calling the start() method that gives us the stage for our JavaFX app. Remember, all the JavaFX’s a stage. (It had to be said.)

In the start() method, we load the convert.fxml file. We haven’t created the convert.fxml file, yet. We will, soon.

FXML files have a Parent as a root. In our case, it is a VBox. But it could be any class that inherits from Parent. A Parent in the scene graph could be a box, pane, or any other container. Look over the selection of Parents in the Container list next time you have Scene Builder open to get a feel for the types of Parents you could use as your root.

You’ll see in the Main class that we don’t include a width and height when configuring the scene to add to the stage. The reason is that we set the VBox width and height in the FXML using Scene Builder.

In short, the start() method loads the FXML description of our window. Then the FXML is placed in a Parent object, which is placed in a Scene, that is finally placed in a Stage and made visible using the show() method.

Simple.

FXML Creation with Scene Builder

Next, let’s get our FXML ready. We’re going to use Scene Builder to create it. However, if you don’t care about Scene Builder, or just want to take a shortcut. Create a file named convert.fxml in the same directory as your Main class and place the following XML into it.

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<VBox 
  alignment="CENTER" 
  maxHeight="-Infinity" 
  maxWidth="-Infinity"
  minHeight="-Infinity" 
  minWidth="-Infinity" 
  prefHeight="186.0"
  prefWidth="272.0" 
  spacing="10.0" 
  xmlns="http://javafx.com/javafx/8.0.65"
  xmlns:fx="http://javafx.com/fxml/1" 
  fx:controller="com.whatisjavafx.ConvertController">
  
  <children>
    <Label 
      text="Fahrenheit to Celsius Converter" 
      textAlignment="CENTER">
      <font>
        <Font name="Futura Bold" size="14.0" />
      </font>
      <VBox.margin>
        <Insets bottom="10.0" top="10.0" />
      </VBox.margin>
    </Label>
    <HBox 
      prefHeight="100.0" 
      prefWidth="200.0" 
      spacing="10.0">
      <children>
        <Label prefWidth="75.0" text="Fahrenheit" />
        <TextField fx:id="fahrenheitTF" />
      </children>
    </HBox>
    <HBox 
      prefHeight="100.0" 
      prefWidth="200.0" 
      spacing="10.0">
      <children>
        <Label prefWidth="75.0" text="Celsius" />
        <TextField fx:id="celsiusTF" />
      </children>
    </HBox>
    <Button 
      fx:id="convertButton" 
      mnemonicParsing="false" 
      onAction="#convertClicked"
      text="Convert F to C">
      <VBox.margin>
        <Insets bottom="10.0" top="10.0" />
      </VBox.margin>
    </Button>
  </children>
  <padding>
    <Insets bottom="10.0" left="10.0" top="10.0" />
  </padding>
</VBox>

 

Before constructing the FXML using Scene Builder, take a look at the provided FXML, above. It starts in the same way as countless XML files, with a version and encoding information. You’ll find that FXML starts with import statements very similar to the way Java classes start.

After the initial imports, you see a very easy-to-read Scene node graph. Each container is listed (like VBox and HBox), followed by its children. The layout is very easy to follow. Most people find the JavaFX layouts presented in FXML formatting to be easier understand than the same layouts given in pure Java.

Here’s how you create that FXML above using Scene Builder. If you have not set up Eclipse to edit FXML files with Scene Builder, yet, open your Eclipse workspace preferences from the Eclipse menu bar, and select JavaFX. You should see a JavaFX dialog allowing you to set the location of your Scene Builder executable. Once the Scene Builder executable is set, it is far easier to open FXML files from inside Eclipse for editing in Scene Builder.

SceneBuilder Setup in Eclipse
Configure Eclipse to open FXML files with Scene Builder.

You won’t need this configuration to create your FXML file. But, if you want to modify your FXML file after you close Scene Builder, this configuration makes your life a little more peaceful.

Go ahead and open your Scene Builder. You should have a blank UI. You create your UI by dragging containers and other nodes from the left onto the middle design area. You configure those nodes using the area on the right.

SceneBuilder
Scene Builder is a simple RAD tool for laying out JavaFX UI’s.

A useful feature on the left is the Hierarchy section. The Hierarchy shows the nodes and their arrangement visually. It makes it easy to see what nodes are where on the UI. The Hierarchy also make is easy to select hard-to-click nodes in your layout.

Scene Builder Hierarchy
Use the Scene Builder hierarchy to quickly tell how your UI is put together.

The Hierarchy above describes the root node for our temperature conversion window. Create a layout similar to this one by dragging a VBox to the center area of SceneBuilder. Then drag two HBoxes and a Button out to fill the first three rows of the VBox. Then drag Labels and TextFields out to the HBoxes.

You should end up with a Hierarchy that looks like that pictured above. Don’t worry that the layout doesn’t look like the final layout, yet. The devil’s in the configurations. We’ll deal with that in a moment. First get the nodes in the scene graph in the correct hierarchy.

At this point any normal person is asking, What are VBoxes and HBoxes? Don’t worry. That’s a normal question.

Labels, Buttons and TextFields are somewhat obvious and self-describing. However, VBox and HBox isn’t quite as simple. VBoxes and HBoxes are what are called Containers. Containers hold things like Labels and Buttons. You add Components to Containers, and the Containers automagically layout the components for you. There is a whole are to choosing and combining Containers in a way that makes your application look professional. Look for other tutorials on this site dealing with that art.

VBox containers layout design elements in a line starting at the top of the window, and going down with each new component added. HBox containers line up components from left to right as they are added. VBox and HBox are only to containers of a whole collection of possible containers that JavaFX provides. However, by combining just these two types of containers, you can come up with some pretty sophisticated UI layouts.

Note: If the first couple of tries at creating the layout for the temperature conversion window don’t work out. Just start fresh by selecting

File->New

from the Scene Builder menu. Nothing to be ashamed of. You’re just learning after all.

Focus first on getting the hierarchy that you want. Once the hierarchy looks correct, fix your labels to say what you want by double clicking on them. Once you double click on the label, you should be able to type your new text directly on it.

Do the same with the Button. Double-click the button in SceneBuilder, and you should be able to modify the button text.

Now you should have a scene graph hierarchy that matches the one above, and the labels and buttons should have text you approve of. Next, we need to fix the layout and hookup the necessary nodes to the controller (that we haven’t created yet.)

Good configuration of elements can turn a so-so layout into a great user experience. Proper padding between elements and between the edges of the windows makes for a layout that is easy on the eyes, and happier users. You’ve been looking at layouts on computers for a long time, so just experiment a bit and you’ll find satisfying combinations of padding, font sizes and such.

Here are the element configurations that I used. Click on each element and make sure the Properties section is expanded on the right hand side to see your current properties configuration.

Converter Title
Select each element and modify the properties section under the Inspector at the right to configure the look of each element.

Here are the properties for the screen elements. You don’t have to match these exactly. Just pick something that looks good to you.

  • Title Label … Text Alignment: centered;Font: Futura 14px (Bold)
  • Top HBox …Alignment: TOP_LEFT
  • Fahrenheit Label … Font: System 13px; Text Alignment: left; Alignment: CENTER_LEFT
  • Fahrenheit TextField … Font: System 13px; Alignment: CENTER_LEFT
  • Bottom HBox …Alignment: TOP_LEFT
  • Celsius Label … Font: System 13px; Text Alignment: left; Alignment: CENTER_LEFT
  • Celsius TextField … Font: System 13px; Alignment: CENTER_LEFT
  • Button … Font: System 13px; Alignment: CENTER_LEFTSpacing breaks up spaces in visually appealing manner, and makes it easier to find related elements. Here are the layouts for the screen elements. Again, you don’t have to match these exactly.
    • Title Label … Margin above and below set to “10”
    • Top HBox … Spacing: 10; Pref Width: 200; Pref Height: 100;
    • Fahrenheit Label … Pref Width: 75;
    • Fahrenheit TextField … Nothing special
    • Bottom HBox … Alignment: TOP_LEFT
    • Celsius Label … Pref Width: 75;
    • Celsius TextField … Nothing special
    • Button … Margin above and below set to “10”

JavaFX Controller

Okay. This part is VERY important, and you need to get it right or it will give you all kinds of headaches.

You want access to the events triggered by that Button, and you want access to the two TextFields in your Java code. In JavaFX, your Controller class is going to handle the TextFields and the Button. However, to access them in your JavaFX Controller, you need to give them fx:id‘s, and the Button needs a name for its onAction method.

In the code section on the right of Scene Builder fill in fx:id values for your fahrenheit TextField, celcius TextField, and your convert Button. To work with the sample Controller below, use the values fahrenheitTFcelsiusTF, and convertButton.

Also, give the Button the On Action value of convertClicked. So, you should have entered two values in the code section for your button–one for the fx:id and one for the On Action.

There is one last area to fill out before saving your FXML file to the same folder as your JavaFX’s Main class. If you are a little fatigued at this point, imagine if you had to write all your JavaFX’s FXML by hand. That would be rough.

The last area of Scene Builder that you need to fill in before creating your JavaFX FXML file is the Controller section on the bottom left-hand side.

Scene Builder Controller
Make sure you have your Scene Builder’s Controller section filled out properly in order to make the FXML and custom Controller interact without any problems.

The one gotcha that always messes things up is naming the Controller class, but not giving the fully qualified package name. If you have just the class name, then when the JRE goes to do all it’s fancy dependency injection you will get an exception stating that the Controller class couldn’t be found.

Finally, here is the code for the ConvertController class.

package com.whatisjavafx;
/**
 * Sample Skeleton for 'convert.fxml' Controller Class
 */

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;

public class ConvertController {

  @FXML
  private ResourceBundle resources;

  @FXML
  private URL location;

  //fx:id="fahrenheitTF"
  // Value injected by FXMLLoader
  @FXML 
  private TextField fahrenheitTF;

  //fx:id="celsiusTF"
  //Value injected by FXMLLoader
  @FXML 
  private TextField celsiusTF; 

  //fx:id="convertButton"
  //Value injected by FXMLLoader
  @FXML 
  private Button convertButton; 

  @FXML
  void convertClicked(ActionEvent event) {
    double fahrenheit;
    double celsius;
    
    String celsiusString = 
        "Fahrenheit temperature needed.";
    
    String fahrenheitString = 
        fahrenheitTF.getText();
    fahrenheit = 
        Double.parseDouble(fahrenheitString);
    
    celsius = (fahrenheit - 32) * (5.0/9.0);
    celsiusString = Double.toString(celsius);
    
    celsiusTF.setText(celsiusString);
  }

  //This method is called by the 
  //FXMLLoader when initialization is 
  //complete. We don't do anything with
  //it in this example.
  @FXML 
  void initialize() {}
}

 

Not too bad. Bet you’d like a little more explanation, though.

The short and easy explanation is that the method and three fx:id’s you set in your Scene Builder when you were creating your FXML are realized through dependency injection in this custom Controller.

The code for doing the actual conversion to Celsius is in the  convertClicked() method. You named that method in Scene Builder, and now you’re using it. Neat.

Note that the initialize() method is called on construction, so put any configuration or initialization code in there.

Summary

In summary, there’s a lot to learn when you are creating JavaFX applications that follow MVP or MVC design patterns. JavaFX with FXML is a lot harder than pure Java JavaFX projects. FXML probably isn’t worth it if you are just fulfilling requirements for a homework assignment or making a simple tool. However, if you are creating a complex enterprise-quality application, it will allow the workload to be spread across multiple teams with specialized skills such as UX and UI designers, business logic and data specialists by separating out skill sets. It will also make maintaining the code easier in five to ten years when no one remembers how it was put together.

Leave a Reply

Your email address will not be published. Required fields are marked *