Numeric Textfield for Integers in Javafx 8 with Textformatter And/Or Unaryoperator

Numeric TextField for Integers in JavaFX 8 with TextFormatter and/or UnaryOperator

The converter is different to the filter: the converter specifies how to convert the text to a value, and the filter filters changes the user may make. It sounds like here you want both, but you want the filter to more accurately filter the changes that are allowed.

I usually find it easiest to check the new value of the text if the change were accepted. You want to optionally have a -, followed by 1-9 with any number of digits after it. It's important to allow an empty string, else the user won't be able to delete everything.

So you probably need something like

UnaryOperator<Change> integerFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches("-?([1-9][0-9]*)?")) {
return change;
}
return null;
};

myNumericField.setTextFormatter(
new TextFormatter<Integer>(new IntegerStringConverter(), 0, integerFilter));

You can even add more functionality to the filter to let it process - in a smarter way, e.g.

UnaryOperator<Change> integerFilter = change -> {
String newText = change.getControlNewText();
// if proposed change results in a valid value, return change as-is:
if (newText.matches("-?([1-9][0-9]*)?")) {
return change;
} else if ("-".equals(change.getText()) ) {

// if user types or pastes a "-" in middle of current text,
// toggle sign of value:

if (change.getControlText().startsWith("-")) {
// if we currently start with a "-", remove first character:
change.setText("");
change.setRange(0, 1);
// since we're deleting a character instead of adding one,
// the caret position needs to move back one, instead of
// moving forward one, so we modify the proposed change to
// move the caret two places earlier than the proposed change:
change.setCaretPosition(change.getCaretPosition()-2);
change.setAnchor(change.getAnchor()-2);
} else {
// otherwise just insert at the beginning of the text:
change.setRange(0, 0);
}
return change ;
}
// invalid change, veto it by returning null:
return null;
};

This will let the user press - at any point and it will toggle the sign of the integer.

SSCCE:

import java.util.function.UnaryOperator;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.IntegerStringConverter;

public class IntegerFieldExample extends Application {

@Override
public void start(Stage primaryStage) {
TextField integerField = new TextField();
UnaryOperator<Change> integerFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches("-?([1-9][0-9]*)?")) {
return change;
} else if ("-".equals(change.getText()) ) {
if (change.getControlText().startsWith("-")) {
change.setText("");
change.setRange(0, 1);
change.setCaretPosition(change.getCaretPosition()-2);
change.setAnchor(change.getAnchor()-2);
return change ;
} else {
change.setRange(0, 0);
return change ;
}
}
return null;
};

// modified version of standard converter that evaluates an empty string
// as zero instead of null:
StringConverter<Integer> converter = new IntegerStringConverter() {
@Override
public Integer fromString(String s) {
if (s.isEmpty()) return 0 ;
return super.fromString(s);
}
};

TextFormatter<Integer> textFormatter =
new TextFormatter<Integer>(converter, 0, integerFilter);
integerField.setTextFormatter(textFormatter);

// demo listener:
textFormatter.valueProperty().addListener((obs, oldValue, newValue) -> System.out.println(newValue));

VBox root = new VBox(5, integerField, new Button("Click Me"));
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 120);
primaryStage.setScene(scene);
primaryStage.show();
}

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

Tutorial

A comprehensive tutorial guide on using a TextFormatter:

  • Pragmatic coding JavaFX TextFormatter tutorial.

What is the recommended way to make a numeric TextField in JavaFX?

Very old thread, but this seems neater and strips out non-numeric characters if pasted.

// force the field to be numeric only
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
}
});

Text Field that accepts only numbers

Problem solved. All I had to do is replacing setTextFormatter with delegateSetTextFormatter and here is the new code:

        UnaryOperator<TextFormatter.Change> integerFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches("-?([1-9][0-9]*)?")) {
return change;
}
return null;
};

MFXTextField.delegateSetTextFormatter(
new TextFormatter<Integer>(new IntegerStringConverter(), null, integerFilter));

Java 8 U40 TextFormatter (JavaFX) to restrict user input only for decimal number

Please see this example:

DecimalFormat format = new DecimalFormat( "#.0" );

TextField field = new TextField();
field.setTextFormatter( new TextFormatter<>(c ->
{
if ( c.getControlNewText().isEmpty() )
{
return c;
}

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

if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
}));
  • Here I used the TextFormatter(UnaryOperator filter) constructor which takes a filter only as a parameter.

  • To understand the if-statement refer to DecimalFormat parse(String text, ParsePosition pos).

JavaFX TextField listener gives java.lang.IllegalArgumentException: The start must be = the end

Using the same idea as the duplicate. You need to define a regular expression that matches binary numbers.

I am using "\\b[01]+\\b" to define binary numbers and "" to define an empty TextField.

MCVE

import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class TestingGroundsTwo extends Application
{
public static void main(String[] args)
{
launch(args);
}

@Override
public void start(Stage stage)
{
UnaryOperator<Change> binaryFilter = change -> {
String newText = change.getControlNewText();
if (newText.matches("\\b[01]+\\b") || newText.matches("")) {
return change;
}
return null;
};
TextField textField = new TextField();
textField.setTextFormatter(new TextFormatter<>(binaryFilter));

stage.setTitle("Hello World!");
Scene scene = new Scene(new StackPane(textField), 750, 125);
scene.setFill(Color.GHOSTWHITE);
stage.setScene(scene);
stage.show();
}
}

Limit TextField to a specific range of number JavaFX?

You can use regex to allow your specific numbers' range(1-19) and add that validation on TextField's TextFormatter's filter.

Regex => ([1-9]|1[0-9])

  • [1-9] Either TextField allows you to enter 1 to 9 numbers
  • 1[0-9] Or TextField allows you to enter 10 to 19 numbers

Regex Circut

Sample Image

TextField Validation Demo

public class TextFieldValidationDemo extends Application {

private static final String REGEX_VALID_INTEGER = "([1-9]|1[0-9])";

@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setCenter(getRootPane());
primaryStage.setScene(new Scene(root, 200, 200));
primaryStage.show();
}

private BorderPane getRootPane() {
BorderPane root = new BorderPane();
root.setCenter(getTextField());
return root;
}

private TextField getTextField() {
TextField field = new TextField();
field.setTextFormatter(new TextFormatter<>(this::filter));
return field;
}

private TextFormatter.Change filter(TextFormatter.Change change) {
if (!change.getControlNewText().matches(REGEX_VALID_INTEGER)) {
change.setText("");
}
return change;
}
}


Related Topics



Leave a reply



Submit