Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an Integer spinner with FXML

I am trying to create an Integer Spinner, but instead, a Double one is created instead.

Test.class

package com.neonorb.test;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class Test extends Application{
    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Demo.fxml"));
        Parent root = fxmlLoader.load();
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

DemoController.class

package com.neonorb.test;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Spinner;

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

public class DemoController implements Initializable{
    @FXML
    private Spinner<Integer> spinner;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        System.out.println(spinner.getValue());
    }
}

Demo.fxml

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Spinner?>
<?import java.lang.Integer?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="com.neonorb.test.DemoController">
    <Spinner fx:id="spinner" min="1" initialValue="1" amountToStepBy="1">
        <max>
            <Integer fx:constant="MAX_VALUE"/>
        </max>
    </Spinner>
</BorderPane>

When I execute it it outputs 1.0 which is how I know it is making a Double spinner.

I guess what is going on is that FXMLLoader is choosing the wrong constructor for Spinner. How do I choose the Integer one?

like image 313
Chris Smith Avatar asked Jul 06 '15 14:07

Chris Smith


People also ask

How do I specify a controller in FXML?

It is also possible to assign the controller class directly from the FXML file. In order to do that, you should first open your FXML file and add the following code on the first line of the file right after declarations. Example: Since my controller is inside the package name “view”, my fx: controller = “view.

Is FXML part of JavaFX?

FXML is an XML-based user interface markup language created by Oracle Corporation for defining the user interface of a JavaFX application. FXML presents an alternative to designing user interfaces using procedural code, and allows for abstracting program design from program logic.


2 Answers

A small update for the FXML file...

Include

<?import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory?>

then

        <Spinner fx:id="spinner" BorderPane.alignment="CENTER" editable="true" >
            <valueFactory>
                <SpinnerValueFactory.IntegerSpinnerValueFactory min="0" max="10" initialValue="5" amountToStepBy="1"/>
            </valueFactory>
        </Spinner>
like image 55
Johntor Avatar answered Oct 05 '22 22:10

Johntor


Problem

You need to set a value factory to the spinner. Otherwise you will be faced with the type coercion. If you don't be able to set it there, you can define Integer values that will called by static valueOf().

The JavaFX Introduction to FXML notes about Type Coercion:

Type Coercion

FXML uses "type coercion" to convert property values to the appropriate type as needed. Type coercion is required because the only data types supported by XML are elements, text, and attributes (whose values are also text). However, Java supports a number of different data types including built-in primitive value types as well as extensible reference types.

The FXML loader uses the coerce() method of BeanAdapter to perform any required type conversions. This method is capable of performing basic primitive type conversions such as String to boolean or int to double, and will also convert String to Class or String to Enum. Additional conversions can be implemented by defining a static valueOf() method on the target type.


Solution with a Factory

There already exists a IntegerSpinnerValueFactory. Because it is a nested class of SpinnerValueFactory you have to use it with a dot in the tag-name.

There are three constructors available, you can set min/max and min/max/initialValue and min/max/initialValue/amountToStepBy. This is done by setting it as an attribute.

Demo.fxml

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


<BorderPane xmlns:fx="http://javafx.com/fxml/1" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="demoapp.DemoController">
  <center>
    <Spinner fx:id="spinner" BorderPane.alignment="CENTER" >
      <valueFactory>
        <SpinnerValueFactory.IntegerSpinnerValueFactory min="0" max="10"/>
      </valueFactory>
    </Spinner>
  </center>
</BorderPane>


Solution without a Factory

You be also able to define two variables and use them as a static valueOf(). As described in the above quote with the static valueOf() method. So your FXMLLoader does not have to guess which type you probably mean. It calls the constructor with int values.

Demo.fxml

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


<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="demoapp.DemoController" xmlns="http://javafx.com/javafx/8.0_40" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0"  >
  <center>
    <fx:define>
      <Integer fx:id="min" fx:value="0"/>
      <Integer fx:id="max" fx:value="10"/>
    </fx:define>
    <Spinner fx:id="spinner" BorderPane.alignment="CENTER" min="$min" max="$max">
    </Spinner>
  </center>
</BorderPane>
like image 23
aw-think Avatar answered Oct 05 '22 21:10

aw-think