← back to the blog


Form Elements

Posted in JavaFX

Welcome to another JavaFX tutorial. In the last tutorial, we learned about Layout containers and how they help us organise our view. In this tutorial, I will be covering the various UI elements and controls that JavaFx offers.

“Labelled” Node class

Lets start by discussing what a labelled node is. Labelled is an abstract Node class in JavaFx which provides the functionality for a subclass Node to have a textual property associated to it, along with a graphic. Radio buttons, check boxes or even a simple button are all examples of Labelled nodes. They have an obvious graphic element and an associated text property (thus aptly named Labelled). The relative alignment of the graphic and text property can be modified using the setAlignment() method. You can further set your own graphic node to a Labelled Node provided that the graphic node doesn’t appear anywhere else in the scene graph. We will see many examples of Labelled node in this tutorial as we move along.

Capturing Text information

Most UI driven application usually require capturing certain textual data. A TextField can be used to capture short textual user inputs which fit in one line. If you need to support multi-line  and long input texts (lets say for supporting a user to write long paragraphs and such), you can make use of a TextArea. Furthermore, if you want to hide the text information being typed in, such as while capturing passwords or pins, you can make use of the PasswordField which, intuitively enough, extends the TextField class. In the example below, we create each of the above. Notice that you can also set placeholder text to a textfield by calling setPrompt(“placeholder”) method.

 


@Override
public void start(Stage primaryStage) throws Exception {
    
    TextField textField=new TextField("Default Text");
    
    TextArea textArea=new TextArea("Default text");
    textArea.setPrefHeight(50);

    PasswordField passwordField=new PasswordField();
    passwordField.setPromptText("Enter password");

    //add those elements to a vbox container
    VBox vBox=new VBox();
    vBox.getChildren().addAll(textField,textArea,passwordField);

    //we will set some padding around the border
    vBox.setPadding(new Insets(5,5,5,5));//an inset defines the border around this container

    //provide some spacing between the children of the vbox
    vBox.setSpacing(10);
    primaryStage.setTitle("Text Input");
    primaryStage.setScene(new Scene(vBox, 300, 275));
    primaryStage.show();
}


Rendering Text

Text node can be used to render text on the view(duh!). Text supports soft wrapping which means you can visually show a long string across multiple lines as though they were being ‘wrapped’. The wrappingWidth of the Text determines the bounds of the wrap. You can otherwise, also hard wrap your Text by delimiting the contained string with ‘\n’. This can be useful in case you want to render several paragraphs with one Text node. Moreover, you can set the font and the alignment properties of the text by calling thesetFont() and setTextAlignment () methods respectively.

Where Text nodes soft wrap to render their content , a Label node truncates the trailing string and replaces it with dot dot dot (example: some long str…)  making it the preferred option for displaying “headings” that are usually fixed and need to fit in a certain area. Label is a Labelled node which means you can supply an optional graphic to your Label such as an ImageView(discussed below). Label nodes also have a labelFor property which allows them to be used as mnemonics: Imagine you have several input controls lined up in a form. More often than not, you would be annotating those controls with a small text that describes them(for example: “First Name” followed by a TextField, “Last Name” followed by a TextField, etc). A mnemonic is a fancy way of saying that a Label is used ‘for’ this particular node. Now whenever focus comes on that Label(lets say by pressing the tab key), it goes directly to the node referred by the labelFor” property.

The example below shows how this works. Note that a Label also support setting of font and alignment just as a Text does.


@Override
public void start(Stage primaryStage) throws Exception {

    Text text=new Text("A very very very very very very very very very very very very " +
            "long string that will get wrapped");
    text.setWrappingWidth(250);
    text.setFont(new Font("Gill Sans",20));

    Label label=new Label("Label for the following field");
    TextField textField=new TextField();
    label.setLabelFor(textField);

    //add those elements to a vbox container
    VBox vBox=new VBox();
    vBox.getChildren().addAll(text,new TextField(),new TextField(),label,textField);

    //we will set some padding around the border
    vBox.setPadding(new Insets(5,5,5,5));//an inset defines the border around this container

    //provide some spacing between the children of the vbox
    vBox.setSpacing(10);
    primaryStage.setTitle("Text Input");
    primaryStage.setScene(new Scene(vBox, 300, 275));
    primaryStage.show();
}

Rendering Images

An ImageView node can display images on the view. The image displayed can either be loaded by supplying the image name as a classpath resource or through the JavaFx’s Image class.The displayed image can be resized by specifying the fitWidth and fitHeight property. While setting these properties you may lose your aspect ratio. In order to preserve it use the preserveAspectRatio property by using its setter. Note that aspect ratio always uses the fitWidth property if both width and height are specified.


    @Override
    public void start(Stage primaryStage) throws Exception {

        Image image = new Image("mountain.png");
        ImageView imageView=new ImageView(image);
        imageView.setPreserveRatio(true);

        //aspect ratio always works width first it it is specified, if not then the height
        imageView.setFitHeight(250);
        //uncommenting this line would make aspect ratio act on the width instead of height
//        imageView.setFitWidth(150);

        Pane pane=new Pane();
        pane.getChildren().addAll(imageView);

        primaryStage.setTitle("Render Image");
        primaryStage.setScene(new Scene(pane, 500, 500));
        primaryStage.show();
    }

Choices and options

To allow the user to select from a finite number of options, a CheckBox can be used. CheckBox is a Labelled node where in, the box acts as the graphic.CheckBox also has an boolean indeterminate property which, if set, allows the user to toggle between three states: checked, unchecked and undefined(which is shown as a dash).

We have already seen the Button node in the previous tutorials. A different type of button called a ToggleButton can used to present an on/off switch. A ToggleButton(which is again a Labelled node) holds a boolean flag called “selected” which indicates if it is selected or not. By default, a ToggleButton exists on its own, but a multitude of ToggleButtons can also be part of a ToggleGroup. A ToggleGroup comprises of a number of ToggleButtons ,wherein at most one ToggleButton can be selected at a time. Keep in mind that a selected ToggleButton could always be toggled off regardless of weather it is in a ToggleGroup or not.To specify a ToggleGroup for a ToggleButton simply call the setToggleGroup()method.

When mutually exclusive options need to be presented, a RadioButton can be used. A RadioButton is a direct subclass of a ToggleButton. But unlike ToggleButtons,once selected, a RadioButton can never be deselected by clicking on it again. It can only be deselected if another RadioButton in the same ToggleGroup gets selected.

 


@Override
public void start(Stage primaryStage) throws Exception {

    //Checkboxes for "interests"
    CheckBox football=new CheckBox("Football");
    CheckBox basketball=new CheckBox("Basketball");
    CheckBox hockey=new CheckBox("Hockey");
    CheckBox cricket=new CheckBox("Cricket");
    cricket.setIndeterminate(true);//allows a third undefined state shown as '-'
    FlowPane checkBoxes = new FlowPane();
    checkBoxes.getChildren().addAll(football,basketball,hockey,cricket);

    //Toggle Buttons for "playback controls"
    ToggleGroup playbackGroup=new ToggleGroup();
    ToggleButton rewind=new ToggleButton("Rewind");
    ToggleButton play=new ToggleButton("Play");
    ToggleButton fastForward=new ToggleButton("Fast forward");
    rewind.setToggleGroup(playbackGroup);
    play.setToggleGroup(playbackGroup);
    fastForward.setToggleGroup(playbackGroup);
    HBox playbackContainer=new HBox();
    playbackContainer.getChildren().addAll(rewind,play,fastForward);

    //Radio buttons for "gender"
    ToggleGroup genderGroup =new ToggleGroup();
    RadioButton male=new RadioButton("Male");
    RadioButton female=new RadioButton("Female");
    male.setToggleGroup(genderGroup);
    female.setToggleGroup(genderGroup);
    HBox genderContainer=new HBox();
    genderContainer.getChildren().addAll(male,female);

    //add those elements to a vbox container
    VBox vBox=new VBox();
    vBox.getChildren().addAll(checkBoxes,playbackContainer,genderContainer);

    //we will set some padding around the border
    vBox.setPadding(new Insets(5,5,5,5));//an inset defines the border around this container

    //provide some spacing between the children of the vbox
    vBox.setSpacing(10);
    primaryStage.setTitle("Render Text");
    primaryStage.setScene(new Scene(vBox, 300, 275));
    primaryStage.show();
}

Button

Lastly, we have, the ever most common element: the button. We have already covered buttons in the last few tutorials, but one thing I never mentioned is the action should happen when a button is clicked. A click on a button is an event which, like many other events in JavaFx is handled by implementing an EventHandler. This is usually through an anonymous class (or if you are in JDK 8 : lambda expressions). In the example below, we simple create a button which upon being clicked, will print “Clicked” on the console.


@Override
public void start(Stage stage) throws Exception {
    //create a button
    Button button=new Button("Click me!");
    button.setLayoutX(170);
    button.setLayoutY(170);

    //Add the event handler that gets fired when this button gets clicked
    button.setOnAction(new EventHandler() {
        @Override
        public void handle(ActionEvent actionEvent) {
            System.out.println("Clicked");
        }
    });

    //just add the above button at the center of a pane
    Pane pane=new Pane();
    pane.getChildren().addAll(button);
    Scene scene=new Scene(pane,400,400);
    stage.setScene(scene);
    stage.show();
}

I will wrap up here. In the next tutorial, we will go over “Observable Values” and how they help maintaining the UI in an updated state .Till then, happy coding.