1

I have a credit card page in my java fx program. I am trying to make it so that the inputs only allow numbers. At the moment it only gives an error if the fields are empty. But no error occurs if text is included?

I have tried changing it from String to integer, but that doesn't work.

public void thankyoupage(ActionEvent actionEvent) throws IOException {
        String cardno = cardtf.getText();
        String expdate1 = expirytf1.getText();
        String expdate2 = expirytf2.getText();
        String cvvnum = cvvtf.getText();

        if (cardno.equals("") || expdate1.equals("") ||
                expdate2.equals("") || cvvnum.equals("")) {
            Alert alert = new Alert(Alert.AlertType.WARNING, "Enter Full Details", ButtonType.OK);

            alert.showAndWait();
        }    else{
            Window mainWindow = confirmbut.getScene().getWindow();
            Parent newRoot = FXMLLoader.load(getClass().getResource("Thankyou.fxml"));
            mainWindow.getScene().setRoot(newRoot);
        }
    }

Any links or changes would be nice.

  • 1
    Maybe [_What is the recommended way to make a numeric TextField in JavaFX?_](https://stackoverflow.com/questions/7555564/what-is-the-recommended-way-to-make-a-numeric-textfield-in-javafx) helps you. – pzaenger Sep 09 '19 at 11:48
  • @pzaenger no, that's much too old - the current way is to use a TextFormatter – kleopatra Sep 09 '19 at 11:51
  • 1
    @kleopatra [Third answer](https://stackoverflow.com/a/12851162/2436655) describes the use of a `TextFormatter`. However, your link looks fine :) – pzaenger Sep 09 '19 at 11:53
  • @pzaenger me pleading guilty of not scrolling ;) – kleopatra Sep 09 '19 at 12:07

3 Answers3

3

You should attach a TextFormatter to your TextField. I have attached a sample on using Decimals - since you are using money, this might make the most sense.

On your text field you simply add the TextFormatter - this will prevent entry of anything other than what you allow.

//For Example
moneyTextField.setTextFormatter(new DecimalTextFormatter(0, 2));

--Below is the control code.

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.function.UnaryOperator;

import javafx.scene.control.TextFormatter;
import javafx.util.StringConverter;

public class DecimalTextFormatter extends TextFormatter<Number> {
    private static DecimalFormat format = new DecimalFormat("#.0;-#.0");

    public DecimalTextFormatter(int minDecimals, int maxDecimals) {
        super(getStringConverter(minDecimals, maxDecimals), 0, getUnaryOperator(maxDecimals, true,-1));
    }

    public DecimalTextFormatter(int minDecimals, int maxDecimals, boolean allowsNegative) {
        super(getStringConverter(minDecimals, maxDecimals), 0, getUnaryOperator(maxDecimals, allowsNegative,-1));
    }

    public DecimalTextFormatter(int minDecimals, int maxDecimals, boolean allowsNegative , int maxNoOfDigitsBeforeDecimal) {
        super(getStringConverter(minDecimals, maxDecimals), 0, getUnaryOperator(maxDecimals, allowsNegative, maxNoOfDigitsBeforeDecimal));
    }

    private static StringConverter<Number> getStringConverter(int minDecimals, int maxDecimals) {
        return new StringConverter<Number>() {
            @Override
            public String toString(Number object) {
                if (object == null) {
                    return "";
                }
                String format = "0.";
                for (int i = 0; i < maxDecimals; i++) {
                    if (i < minDecimals) {
                        format = format + "0";
                    } else {
                        format = format + "#";
                    }
                }
                format = format + ";-" + format;
                DecimalFormat df = new DecimalFormat(format);
                String formatted = df.format(object);
                return formatted;
            }

            @Override
            public Number fromString(String string) {
                try {
                    if (string == null) {
                        return null;
                    }
                    return format.parse(string);
                } catch (ParseException e) {
                    return null;
                }
            }
        };
    }

    private static UnaryOperator<javafx.scene.control.TextFormatter.Change> getUnaryOperator(int maxDecimals,
            boolean allowsNegative, int noOfDigitsBeforeDecimal) {
        return new UnaryOperator<TextFormatter.Change>() {
            @Override
            public TextFormatter.Change apply(TextFormatter.Change change) {
                if (!allowsNegative && change.getControlNewText().startsWith("-")) {
                    return null;
                }

                if (change.getControlNewText().isEmpty()) {
                    return change;
                }

                ParsePosition parsePosition = new ParsePosition(0);
                Object object = format.parse(change.getControlNewText(), parsePosition);

                if (change.getCaretPosition() == 1) {
                    if (change.getControlNewText().equals(".")) {
                        return change;
                    }
                }

                if (object == null || parsePosition.getIndex() < change.getControlNewText().length()) {
                    return null;
                } else {

                    if(noOfDigitsBeforeDecimal != -1)
                    {
                        int signum = new BigDecimal(change.getControlNewText()).signum();
                        int precision = new BigDecimal(change.getControlNewText()).precision();
                        int scale = new BigDecimal(change.getControlNewText()).scale();

                        int val = signum == 0 ? 1 : precision - scale;
                        if (val > noOfDigitsBeforeDecimal) {
                            return null;
                        }
                    }

                    int decPos = change.getControlNewText().indexOf(".");
                    if (decPos > 0) {
                        int numberOfDecimals = change.getControlNewText().substring(decPos + 1).length();
                        if (numberOfDecimals > maxDecimals) {
                            return null;
                        }
                    }
                    return change;
                }
            }
        };
    }
}
purring pigeon
  • 4,141
  • 5
  • 35
  • 68
0

You have to use regular expressions to validate fields. You can learn more about regular expression here https://regexr.com/

 String cardno = cardtf.getText();


 if (cardno.equals("") || expdate1.equals("") || expdate2.equals("") || cvvnum.equals("")) {
     Alert alert = new Alert(Alert.AlertType.WARNING, "Enter Full Details", ButtonType.OK);            
     alert.showAndWait();
 }else if (cardno.matches("/^[A-Za-z ]+$/")){
     Alert alert = new Alert(Alert.AlertType.WARNING, "It Can not contain letters", ButtonType.OK);            
     alert.showAndWait();
 }else{
     //Else Part
 }
Ahamed Safnaj
  • 611
  • 1
  • 6
  • 18
0

Here is a piece of code that should help you doing the trick by checking at every input if the text contains only numbers an a maximum of one "," as the decimal separator.

There is already a post showing how to do this. Post

import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;

public class NumberField extends TextField {

public NumberField () {
    initSpellListener();
}

public final void initSpellListener() {
    this.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
        if (!newValue.matches("\\d*")) {
            this.setText(newValue.replaceAll("[^\\d,]", ""));/*The comma here "[^\\d,]" can be changed with the dot*/
            StringBuilder aus = new StringBuilder();
            aus.append(this.getText());
            boolean firstPointFound = false;

            for (int i = 0; i < aus.length(); i++) {
                if (aus.charAt(i) == ',') {/*Change the , with . if you want the . to be the decimal separator*/
                    if (!firstPointFound) {
                        firstPointFound = true;
                    } else {
                        aus.deleteCharAt(i);
                    }
                }
            }
            newValue = aus.toString();
            this.setText(newValue);
        } else {
            this.setText(newValue);
        }
    });
}}

[As soon as I find the post I will credit this code.]

if (!newValue.matches("\\d*"))

this part of the code checks with a regex expression if the new string value doesn't contain only numbers, and then with this code

this.setText(newValue.replaceAll("[^\\d,]", ""));

it replaces all the non-digit or comma chars.

Finally the for-loop checks if only exists one comma ad if other are found they are deleted.

To help you with regex writing here is a very useful site : Online regex


Then you can use this object as a normal TextField:

@FMXL
private NumberField nf;
  • 2
    no: a) don't change the property while listening, instead use a TextFormatter b) don't do any manual pattern matching for numbers, instead use NumberFormat – kleopatra Sep 09 '19 at 13:49