Vous êtes ici

My first JavaFX application - 2 - First UI

javaPrevious article

It's now time to write some code. I'll do what I usually do when I have to develop an application with a GUI: I design the UI before anything else.

So, firstly, let's list the functions the UI has to provide:

  • let the user choose a serial port (speed and byte format are fixed by the device)
  • let the user send some frames to the device, after having set up some parameters depending on the frame. Our first version will only send one predetermined frame
  • display received frames

Now that we know what we want to do, let's implement it! After some additional reading and tests, I end up with following architecture and code:

  • GUI is designed using Scene Builder. Resulting FXML code is in file UserInterface.fxml. This UI is based on following elements:
    • a label and a text field (not used in current code)
    • a button
    • a list view
  • the controller associated to the UI is implemented by UserInterfaceController class
  • RecFrames class is used by the controller to handle data displayed by the list view
  • Main class starts the application

Contents of each source file is below:

UserInterface.xml

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" 
	prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1"
	fx:controller="com.monblocnotes.devicecontroller.UserInterfaceController">
   <children>
      <GridPane>
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <Label text="Enter serial port device:" />
            <TextField fx:id="serialPortTF" GridPane.columnIndex="1" />
            <Button fx:id="sendReadCommandBtn" mnemonicParsing="false" text="Send READ command" GridPane.rowIndex="1" />
         </children>
      </GridPane>
      <ListView fx:id="recFramesLV" prefHeight="319.0" prefWidth="600.0" />
   </children>
</VBox>

UserInterfaceController.java

package com.monblocnotes.devicecontroller;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;

public class UserInterfaceController implements Initializable {
	
	@FXML private TextField serialPortTF;
	@FXML private Button sendReadCommandBtn;
	@FXML private ListView<String> recFramesLV;

	private ObservableList<String> lines;
	private RecFrames recFrames;
	
	private int num;

	/**
	 * 
	 */
	@Override
	public void initialize(URL location, ResourceBundle resources) {

		sendReadCommandBtn.setOnAction(new EventHandler<ActionEvent>() {
			
			@Override
			public void handle(ActionEvent event) {

				addFrame();
				
			}
		});

		lines = FXCollections.observableArrayList();
		recFramesLV.setItems(lines);
		recFrames = new RecFrames(lines);
		
		num = 0;

	}

	/**
	 * 
	 */
	public void addFrame() {

		num++;
		String str = "frame " + num;
		recFrames.addFrame(str);

	}

}

RecFrames.java

package com.monblocnotes.devicecontroller;

import javafx.collections.ObservableList;

public class RecFrames {
	
	// Maximum number of displayed frames.
	private final static int MAX_NB_FRAMES = 3;
	
	private ObservableList<String> frameList;

	/**
	 * 
	 * @param frameList
	 */
	public RecFrames(ObservableList<String> frameList) {
		
		this.frameList = frameList;
		
	}
	
	/**
	 * 
	 * @param frame
	 */
	public void addFrame(String frame) {
		
		int s = frameList.size();
		if (s >= MAX_NB_FRAMES) {
			// Remove oldest element.
			frameList.remove(s - 1);
		}
		// Add new element.
		frameList.add(0, frame);
		
	}

}

Main.java

package com.monblocnotes.devicecontroller;
	
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;

public class Main extends Application {
	
	/**
	 * 
	 */
	@Override
	public void start(Stage primaryStage) {
		
		try {
			Parent root = FXMLLoader.load(getClass().getResource("UserInterface.fxml"));
			Scene scene = new Scene(root,400,400);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			primaryStage.setScene(scene);
			primaryStage.show();
		} catch(Exception e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		
		launch(args);
		
	}
	
}

What happens when you run this program? The window below is displayed:

version1EmptyWindowQuite ugly, isn't it? Embarassed But it does what I want with this first version, i.e. intercepting clicks on button, and displaying most recent strings in list view. More precisely, every time you click on the Send READ command button, a new string is generated and displayed. Most recent string is displayed at top, and only latest three strings are displayed:

version1ThreeFramesWindow

Why this name for the button? Well, you'll discover later. 

Next article