How to set content of a JavaFx TextField with TextFormatter

Viewed 479

This is a new question that follows on my previous one called "How to clear a JavaFx TextField with TextFormatter". To the small test application (Windows 10, JDK 1.8) of the previous question I added an other button to set the TextFields to some content. It does not work ! What should I do ?

Here is the revised code:

package example;

import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class DoubleFieldExample extends Application {
    @Override
    public void start(Stage primaryStage) {
        final TextField field = new TextField();
        final TextField field2 = new TextField();
        final Button clearBtn = new Button("Clear Text Fields");
        final Button setValueBtn = new Button("Set Text Fields Value");
        final Double defaultValue = null;

        final UnaryOperator<TextFormatter.Change> numberOnlyFilter = change -> {
//        String regexp = "[-+]?(\\b[0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+\\b)?";
        String regexp = "[0-9.eE+-]";
            final String text = change.getText();
            return (text.isEmpty() || text .matches(regexp)) ? change : null;
        };

        final StringConverter<Double> NumberConverter = new StringConverter<Double>() {
            @Override
            public String toString(Double value) {
//                return (value == null) ? null : value.toString();
                return (value == null) ? "" : value.toString();
            }
            @Override
            public Double fromString(String text) {
                return (text == null || text.trim().isEmpty()) ? null : Double.parseDouble(text.trim());
            }
        };

        final TextFormatter<Double> numberOnlyFormatterField1 = new TextFormatter<Double>(NumberConverter, defaultValue, numberOnlyFilter);
        final TextFormatter<Double> numberOnlyFormatterField2 = new TextFormatter<Double>(NumberConverter, defaultValue, numberOnlyFilter);
        field.setTextFormatter(numberOnlyFormatterField1);
        field2.setTextFormatter(numberOnlyFormatterField2);
        numberOnlyFormatterField1.valueProperty().addListener((observable, oldValue, newValue) -> System.out.printf("%s -> %s%n", oldValue, newValue));
        clearBtn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                field.setText("");
                field2.clear();
            }
        });
        setValueBtn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                field.setText("123.45");
                field2.setText("1.25e+4");
            }
        });
        final StackPane root = new StackPane();
        root.getChildren().addAll(new VBox(field, field2, clearBtn, setValueBtn));
        final Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("Test");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

I have the answer, but note that it is kind of Hacking.

You can create your own Formatter:

  class YourFormatter extends TextFormatter<T> {

       static Pattern pattern = Pattern.compile(<regex>);

       YourFormatter(TextField textField) {
           super(getConverter(), defaultValue, getChangeUnaryOperator(textField);
       }

       private static StringConverter<T> getConverter() {
            return new StringConverter<T>() {

                @Override
                public T fromString(String s) {
                    return T.fromString(s);
                }

                @Override
                public String toString(T t) {
                    return t.toString;
                }
           };
       }

       private static UnaryOperator<Change> getChangeUnaryOperator(TextField textField) {
           return c -> {
               String text = c.getControlNewText();
               if (!pattern.matcher(text).matches()) {
              
                  textField.setText(<change>);
                  c.setText("");

               }
               return t;
           };
       }
    
  }


That's how I have resolved the problem...
but maybe there are better (no answer for almost 2 years)

Related