1
votes

I am new to Netbeans, JavaFX and Scene Builder. I use latest versions, meaning 8.x. I have a requirement, capture from and to years. Years are 4 digits long and should be numeric.

I found a solution and tested it as a standalone demo. So I created the following FXML and a JAVA to create a JAR which I can import in Scene Builder.

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

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

    <fx:root id="AnchorPane" prefHeight="30.0" prefWidth="150.0"       type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="numbertextfield.NumberTextField">
    <children>
          <TextField layoutX="14.0" layoutY="14.0" prefHeight="30.0"  prefWidth="150.0" promptText="Enter only numbers " AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
       </children>
    </fx:root>

the Controller code is

package numbertextfield;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;

/**
 *
 * @author Hornigold Arthur
 */
public class NumberTextField extends TextField {

    private final IntegerProperty maxLength = new SimpleIntegerProperty(this, "maxLength", -1);
    private final StringProperty restrict = new SimpleStringProperty(this, "restrict");

    @FXML
    private TextField numberTextField;

    public void NumberTextFieldController() {
        // TODO
        System.out.println(" Inside NumberTextField Controller");

        numberTextField.textProperty().addListener(new ChangeListener<String>() {
            private boolean ignore;

            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String s1) {

                if (ignore || s1 == null) {
                    return;
                }
                if (maxLength.get() > -1 && s1.length() > maxLength.get()) {
                    ignore = true;
                    numberTextField.setText(s1.substring(0, maxLength.get()));
                    ignore = false;
                }

                if (restrict.get() != null && !restrict.get().equals("") && !s1.matches(restrict.get() + "*")) {
                    ignore = true;
                    numberTextField.setText(s);
                    ignore = false;
                }
            }
        });
//
       FXMLLoader fxmlLoader = new FXMLLoader(
                getClass().getResource("/NumberTextField.fxml"));      

        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
//       
    }

    /**
     *      * The max length property.      *      * @return The max le
     * @return ngth
     * property.      
     */
    public IntegerProperty maxLengthProperty() {
        return maxLength;
    }

    /**
     *      * Gets the max length of the text field.      *      * @return The
     * max length.      
     * @return 
     */
    public int getMaxLength() {
        return maxLength.get();
    }

    /**
     *      * Sets the max length of the text field.      *      * @param
     * maxLength The max length.
     * @param maxLength
     */
    public void setMaxLength(int maxLength) {
        this.maxLength.set(maxLength);
    }

    /**
     *      * The restrict property.      *      * @return The restrict
     * property.
     * @return 
     */
    public StringProperty restrictProperty() {
        return restrict;
    }

    /**
     *      * Gets a regular expression character class which restricts the user
     * input.        *      * @return The regular expression.      * @see
     * #getRestrict()
     
     * @return 
     */
    public String getRestrict() {
        return restrict.get();
    }

    /**
     *      * Sets a regular expression character class which restricts the user
     * input.        * E.g. [0-9] only allows numeric values.      *      
     *
     *
     * @param restrict The regular expression.      
     */
    public void setRestrict(String restrict) {
        this.restrict.set(restrict);
    }
}

I imported the jar file in Scene Builder as a custom class and built a FXML for a stand alone module.

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

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

    <AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
       <children>
          <NumberTextField fx:id="testNumber" layoutX="226.0" layoutY="125.0" maxLength="4" restrict="&quot;[0-9]&quot;" />
       </children>
    </AnchorPane>

Following Java code is supposed to load this FXML (Which it does correctly and displays the NumberTextField. But it allows non numeric characters of any length.

    package anothertest;

    import java.io.IOException;
    import javafx.application.Application;
    import static javafx.application.ConditionalFeature.FXML;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    import numbertextfield.NumberTextField;

    /**
     *
     * @author Hornigold Arthur
     */
    public class AnotherTest extends Application {

        @FXML
        NumberTextField testNumber = new NumberTextField();

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

            try {
                FXMLLoader loader = new FXMLLoader();
                loader.setLocation(AnotherTest.class.getResource("AnotherTest.fxml"));
                AnchorPane rootLayout = (AnchorPane) loader.load();
                testNumber.setMaxLength(4);
                testNumber.setRestrict("[0-9");

                // Show the scene containing the root layout.
                Scene scene = new Scene(rootLayout);
                mainStage.setScene(scene);
                mainStage.show();

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            launch(args);
        }

    }

But the following code Works PERFECTLY which I took from Internet samples.

package testnumberinput;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;

/**
 *
 * @author Hornigold Arthur
 */
public class TestNumberInput extends Application {

    @Override
    public void start(Stage primaryStage) {
        RestrictiveTextField textInput = new RestrictiveTextField();
        textInput.setMaxLength(4);
        textInput.setRestrict("[0-9]");
        Label label1 = new Label("Enter a number  : ");
        textInput.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });

        HBox hBox = new HBox();
        hBox.getChildren().addAll(label1, textInput);
        StackPane root = new StackPane();
        root.getChildren().add(hBox);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("This is a test ..... !");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

/**
 *  * A text field, which restricts the user's input.  
 * <p>
 *  * The restriction can either be a maximal number of characters which the
 * user is allowed to input  * or a regular expression class, which contains
 * allowed characters.  * </p>
 *  
 * <p/>
 *  * <b>Sample, which restricts the input to maximal 10 numeric characters</b>:
 *  
 * <pre>
 *  * {@code
 *  * RestrictiveTextField textField = new RestrictiveTextField();
 *  * textField.setMaxLength(10);
 *  * textField.setRestrict("[0-9]");
 *  * }
 *  * </pre>  *  * @author Christian Schudt  
 */
class RestrictiveTextField extends TextField {

    private IntegerProperty maxLength = new SimpleIntegerProperty(this, "maxLength", -1);
    private StringProperty restrict = new SimpleStringProperty(this, "restrict");

    public RestrictiveTextField() {
        textProperty().addListener(new ChangeListener<String>() {
            private boolean ignore;

            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String s1) {
                if (ignore || s1 == null) {
                    return;
                }
                if (maxLength.get() > -1 && s1.length() > maxLength.get()) {
                    ignore = true;
                    setText(s1.substring(0, maxLength.get()));
                    ignore = false;
                }

                if (restrict.get() != null && !restrict.get().equals("") && !s1.matches(restrict.get() + "*")) {
                    ignore = true;
                    setText(s);
                    ignore = false;
                }
            }
        });
    }

    /**
     *      * The max length property.      *      * @return The max length
     * property.      
     */
    public IntegerProperty maxLengthProperty() {
        return maxLength;
    }

    /**
     *      * Gets the max length of the text field.      *      * @return The
     * max length.      
     */
    public int getMaxLength() {
        return maxLength.get();
    }

    /**
     *      * Sets the max length of the text field.      *      * @param
     * maxLength The max length.
     */
    public void setMaxLength(int maxLength) {
        this.maxLength.set(maxLength);
    }

    /**
    *      * The restrict property.      
    *      * @return The restrict property.
    */

    public StringProperty restrictProperty() {
        return restrict;
    }

    /**
     *      * Gets a regular expression character class which restricts the user
     * input.        *      * @return The regular expression.      * @see
     * #getRestrict()
     
     */
    public String getRestrict() {
        return restrict.get();
    }

    /**
     *      * Sets a regular expression character class which restricts the user
     * input.        * E.g. [0-9] only allows numeric values.      *      
     *
     *
     * @param restrict The regular expression.      
     */
    public void setRestrict(String restrict) {
        this.restrict.set(restrict);
    }
    }

Can someone tell me where am I making the mistake ? Thanks a lot.

I MODIFIED THE WHOLE THING AND PUT THEM IN A SINGLE NETBEANS PROJECT FOR CONVENIENCE, TO SEE WHETHER IT WORKS. TELL ME WHETHER THE FOLLOWING PIECES ARE CORRECT. IT STILL DOES NOT WORK. THE FIELD "periodFrom" ALLOWS ME TO TYPE EVERYTHING.

This is fxml-1. (EditedNumber.fxml)

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

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


<fx:root fx:id="numberField" promptText="Enter Number" type="TextField" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" />

This is the controller for fxml-1. (EditedNumber.java)

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package InputNumber;

import java.io.IOException;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TextField;

/**
 *
 * @author Hornigold Arthur
 */
public class EditedNumber extends TextField{

/**
 *
 * @author Hornigold Arthur
 */
    private final IntegerProperty maxLength = new SimpleIntegerProperty(this, "maxLength", -1);
    private final StringProperty restrict = new SimpleStringProperty(this, "restrict");

    @FXML
    private TextField numberField;


    public void EditedNumber() {
        // TODO
        System.out.println(" Inside NumberTextField Controller");

        numberField = new TextField();

        numberField.textProperty().addListener(new ChangeListener<String>() {
            private boolean ignore;

            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String s1) {

                if (ignore || s1 == null) {
                    return;
                }
                if (maxLength.get() > -1 && s1.length() > maxLength.get()) {
                    ignore = true;
                    numberField.setText(s1.substring(0, maxLength.get()));
                    ignore = false;
                }

                if (restrict.get() != null && !restrict.get().equals("") && !s1.matches(restrict.get() + "*")) {
                    ignore = true;
                    numberField.setText(s);
                    ignore = false;
                }
            }
        });
//
        FXMLLoader fxmlLoader = new FXMLLoader(
                getClass().getResource("Editor.fxml"));

        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }
//       
    /**
     *      * The max length property.      *      * @return The max le
     *
     * @return      
     */
    public IntegerProperty maxLengthProperty() {
        return maxLength;
    }

    /**
     *      * Gets the max length of the text field.      *      * @return The
     * max length.      
     *
     * @return
     */
    public int getMaxLength() {
        return maxLength.get();
    }

    /**
     *      * Sets the max length of the text field.      *      * @param
     * maxLength The max length.
     *
     * @param maxLength
     */
    public void setMaxLength(int maxLength) {
        this.maxLength.set(maxLength);
    }

    /**
     *      * The restrict property.      *      * @return The restrict
     * property.
     *
     * @return
     */
    public StringProperty restrictProperty() {
        return restrict;
    }

    /**
     *      * Gets a regular expression character class which restricts the user
     * input.        *      * @return The regular expression.      * @see
     * #getRestrict()      
     *
     * @return
     */
    public String getRestrict() {
        return restrict.get();
    }

    /**
     *      * Sets a regular expression character class which restricts the user
     * input.        * E.g. [0-9] only allows numeric values.      *      
     *
     *
     * @param restrict The regular expression.      
     */
    public void setRestrict(String restrict) {
        this.restrict.set(restrict);
    }

}

MY ASSUMPTION IS THAT -TOGETHER THEY SHOULD WORK IN A TEAM FOR A NEW UI TYPE.

This is the fxml-2. It uses the EditedNumber UI type. (mainFXML.fxml)

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

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


<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
   <children>
      <Label layoutX="128.0" layoutY="104.0" text="Period From : " textAlignment="RIGHT">
         <font>
            <Font name="Bookman Old Style" size="18.0" />
         </font>
      </Label>
      <EditedNumber fx:id="periodFrom" layoutX="255.0" layoutY="98.0" style="-fx-background-color: lightblue; -fx-border-color: red; -fx-border-width: 2;">
         <font>
            <Font name="Bookman Old Style" size="18.0" />
         </font>
      </EditedNumber>
   </children>
</AnchorPane>

This is the controller (or the main program) (InputNumber.java)

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package InputNumber;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

/**
 *
 * @author Hornigold Arthur
 */
public class InputNumber extends Application {

    @FXML
    EditedNumber periodFrom;

    @Override
    public void start(Stage mainStage) {
        periodFrom = new EditedNumber();
        periodFrom.setMaxLength(4);
        periodFrom.setRestrict("[0-9]");

        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(InputNumber.class.getResource("mainFXML.fxml"));
            AnchorPane rootLayout = (AnchorPane) loader.load();

            Scene scene = new Scene(rootLayout);
            mainStage.setScene(scene);
            mainStage.show();


        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

There are no compilation errors. If this works fine, then I can move the EDITED NUMBER Class to Scene Builder and see whether it works correctly.

Thanks in advance for your guidance.

1

1 Answers

1
votes

You got a little mess here..

Lets start with your Main class. testNumber.setRestrict("[0-9"); cannot work, you need to close the bracket: ("[0-9]")

Then in your NumberTextField FXML you define fx:root id=AnchorPane but in your class you extend a TextField. Decide which one you like to extend! I recommend to extend TextField but then you do not need an extra private Field TextField in your class, because then you would have two TextFields.

As controller you wrote controller="numbertextfield.NumberTextField" but this is no specific Classname. In your class you define the controller again. You cannot set it twice! Please decide again where you like to put these lines.

For the TextField you did not define a fx:id but in your class you want to access it via @FXML tag. Also not possible.

Then in the NumberField class you do have this method called NumberTextFieldController which is never called. The code inside must be in the constructor of NumberTextField and not an extra method. Then you must load the FXML first before you can access the textproperty of your TextField. So move up those lines with your FXMLLoader.

Please do some clean up and then try again!

EDIT:

Still lot of mistakes! Maybe you should reed this documentation.

EditedNumber class: I guess you instantiated the numberField because you got a NPE in the next line? Remove that line and move up your fxmlLoader.load() to the first lines of your constructor, so your fxml file gets loaded and the field would be instantiated but you even do not need the field numberField, because your class is the TextField. Remove it and change the calls e.g. from numberField.textProperty() to this.textProperty()

You still got no constructor. Remove the void here

public void EditedNumber() {...}

Why are you loading this resource? You say that the name is EditedNumber.fxml and not Editor.fxml. You need to add a / in as first char in order to find the fxml.

final FXMLLoader fxmlLoader = new FXMLLoader(this.getClass().getResource("Editor.fxml"));

doing all these changes, in my example it worked!!