3

On the Internet, I found very useful class, using which I can restrict TextField. I encountered a problem, where my TextField can contain only one '.' character. I suspect that I can handle this by writing an appripriate regex and set it as a restriction on the instance of that class. I use the following regex: "[0-9.-]", but it allows as many dots as the user types. May I ask you to help me to configure my TextField so that no more than one '.' is allowed.

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; /** * Created by Anton on 7/14/2015. */ public class RestrictiveTextField extends TextField { private IntegerProperty maxLength = new SimpleIntegerProperty(this, "maxLength", -1); private StringProperty restrict = new SimpleStringProperty(this, "restrict"); public RestrictiveTextField() { super("0"); 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); } 

}

1
  • Please provide an examples of valid and invalid inputs Commented Jul 16, 2015 at 15:28

3 Answers 3

9

There are various versions of the regex, depending on exactly what you want to support. Note that you don't only want to match valid numbers, but also partial entries, because the user has to be able to edit this. So, for example, an empty string is not a valid number, but you certainly want the user to be able to delete everything that's there while they are editing; similarly you want to allow "0.", etc.

So you probably want something like

Optional minus sign, followed by either any number of digits, or at least one digit, a period (.), and any number of digits.

The regex for this could be -?((\\d*)|(\\d+\.\\d*)). There are probably other ways to do this, some of them perhaps more efficient. And if you want to support exponential forms ("1.3e12") it gets more complex.

To use this with a TextField, the recommended way is to use a TextFormatter. The TextFormatter consists of two things: a converter to convert between the text and the value it represents (a Double in your case: you can just use the built-in DoubleStringConverter), and vice versa, and then a filter. The filter is implemented as a function that takes a TextFormatter.Change object and returns an object of the same type. Typically you either leave the Change object as it is and return it (to accept the Change "as is"), or modify it somehow. It is also legal to return null to represent "no change". So in your simple case here, just examine the new proposed text, see if it matches the regular expression, return the change "as is" if it matches and return null otherwise.

Example:

import java.util.regex.Pattern; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.TextField; import javafx.scene.control.TextFormatter; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import javafx.util.converter.DoubleStringConverter; public class NumericTextFieldExample extends Application { @Override public void start(Stage primaryStage) { TextField textField = new TextField(); Pattern validDoubleText = Pattern.compile("-?((\\d*)|(\\d+\\.\\d*))"); TextFormatter<Double> textFormatter = new TextFormatter<Double>(new DoubleStringConverter(), 0.0, change -> { String newText = change.getControlNewText() ; if (validDoubleText.matcher(newText).matches()) { return change ; } else return null ; }); textField.setTextFormatter(textFormatter); textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> { System.out.println("New double value "+newValue); }); StackPane root = new StackPane(textField); root.setPadding(new Insets(24)); primaryStage.setScene(new Scene(root)); primaryStage.show(); } public static void main(String[] args) { launch(args); } } 
Sign up to request clarification or add additional context in comments.

2 Comments

thank you for your prompt and exhaustive response! Now I see that it's a lot better to use TextFormatter. Could you help me once more: is it possible to use DigDecomal value with a TextField if the format of the TextField text must be like: "-302,855,453,645,992.06458" or exponential form? How correctly convert text form field into a BigDecimal and vice versa?
Yes: you would need to modify the regex to allow for the commas, and you would need to write your own StringConverter implementation and use it in place of the DoubleStringConverter.
1

You can use the following regex,

"[^\\.]*\\.{0,1}[^\\.]" 

Or as VGR has pointed out, "A period has no special meaning inside character class brackets and 0 or 1 time can be represented with a '?' (question mark).", so you can also use,

"[^.]*\\.?[^.]" 

I don't know why but your class seems to append a * to the regex, so the above regex will effectively become,

"[^\\.]*\\.{0,1}[^\\.]*" 

Which means,

  1. It will allow any character except a . 0 or more times (greedy).
  2. It will allow a . 0 or 1 times.
  3. It will allow any character except a . 0 or more times (greedy).

This is what you seem to be needing. DEMO

2 Comments

A period has no special meaning inside character class brackets, so you can shorten that to "[^.]*\\.?[^.]*".
thanks a lot! Could you also help me to write regex that maches this string: 1,858,653,333.9876767
0
{[0-9]+\\.[0-9]+} 

to match any number if this is in fact what you are wanting to do

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.