Числовое текстовое поле для целых чисел в JavaFX 8 С TextFormatter и / или UnaryOperator

Я пытаюсь создать числовое текстовое поле для целых чисел с помощью TextFormatter JavaFX 8.

решение с UnaryOperator:

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

myNumericField.setTextFormatter(new TextFormatter<String>(integerFilter));

решение с IntegerStringConverter:

myNumericField.setTextFormatter(new TextFormatter<>(new IntegerStringConverter()));  

оба решения имеют свои собственные проблемы. С помощью UnaryOperator я могу вводить только цифры от 0 до 9, как предполагалось, но мне также нужно ввести отрицательные значения, такие как" -512", где знак разрешен только на первом позиция. Также мне не нужны такие номера, как "00016", которые все еще возможны.

метод IntegerStringConverter работает лучше: каждый неверный номер "-16-123" не принято и цифры "0123" преобразуются в "123". Но преобразование происходит только при вводе текста (нажатием enter) или когда текстовое поле теряет фокус.

есть ли способ обеспечить преобразование второго метода с помощью IntegerStringConverter каждый раз, когда значение текстовое поле обновлено?

2 ответов


преобразователь отличается от фильтра: преобразователь определяет, как преобразовать текст в значение, и фильтры фильтра могут вносить изменения пользователь. Похоже, здесь вы хотите оба, но вы хотите, чтобы фильтр более точно фильтровал разрешенные изменения.

обычно мне проще всего проверить новое значение текста, если изменение было принято. Вы хотите дополнительно иметь -, следовал по 1-9 С любым количеством цифр после нее. Важно разрешить пустую строку, иначе пользователь не сможет удалить все.

так что вам, вероятно, нужно что-то вроде

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));

вы даже можете добавить больше функциональности в фильтр, чтобы он обрабатывал - умнее, например

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;
};

это позволит пользователю нажать - в любой момент и он будет переключать знак целое число.

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);
    }
}

 TextField txtpoint = new TextField();
    txtpoint.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
            if (!newValue.isEmpty()) {
                try {
                    long pointI = Integer.parseInt(newValue);
                    txtpoint.setText(String.valueOf(pointI));
                } catch (Exception e) {
                    txtpoint.clear();
                    txtpoint.setText(getNumber(oldValue));
                }
            }
        }
    });


private String getNumber(String value) {
    String n = "";
    try {
        return String.valueOf(Integer.parseInt(value));
    } catch (Exception e) {
        String[] array = value.split("");
        for (String tab : array) {
            try {
                System.out.println(tab);
                n = n.concat(String.valueOf(Integer.parseInt(String.valueOf(tab))));
            } catch (Exception ex) {
                System.out.println("not nomber");
            }
        }
        return n;
    }
}