Wie erzwinge ich eine doppelte Eingabe in einem TextField in JavaFX?

Lesezeit: 7 Minuten

Benutzer-Avatar
Robb1

So stellen Sie sicher, dass ein Benutzer gibt nur doppelte Werte ein in einem bestimmten TextField?

Ich habe eine Lösung für ganze Zahlen gefunden, aber ich kann sie nicht für Doubles verwenden. Was soll ich statt dessen schreiben "\\d*" und "[^\\d]" damit es für Doppel funktioniert?

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]", ""));
        }
    }
});

  • Verwenden Sie keinen Zuhörer; andere Beobachter der Texteigenschaft sehen die ungültige Eingabe, bevor sie zurückgesetzt wird. Benutze einen TextFormatter stattdessen.

    – James_D

    31. August 2017 um 12:04 Uhr

Benutzer-Avatar
James_D

Der Ansatz, einen Listener zu verwenden und zu gültigen Werten zurückzukehren, wenn ungültige Werte vom Benutzer eingegeben werden, funktioniert, kann jedoch zu Problemen führen, wenn Sie andere Listener in den Textfeldern haben textProperty. Diese Listener werden sowohl die ungültigen als auch die gültigen Werte beobachten, also müssen sie wissen, dass sie alle ungültigen Werte herausfiltern müssen.

Ein besserer Ansatz ist die Verwendung von a TextFormatter. Das TextFormatter kann zwei Dinge tun:

  1. Definieren Sie einen “Filter”, der alle an der vorgenommenen Änderungen ablehnen oder ändern kann TextFields Text
  2. Definieren Sie einen “Konverter”, der definiert, wie der Text in und aus Werten eines bestimmten Typs konvertiert wird (z Double) in Ihrem Fall.

Das Definieren des geeigneten Filters kann schwierig sein: Sie möchten alle sinnvollen Änderungen durch den Benutzer zulassen. Das bedeutet, dass der Text möglicherweise in einem ungültigen Zustand ist, während der Benutzer ihn bearbeitet; zB möchten Sie wahrscheinlich zulassen, dass das Textfeld vollständig leer ist, obwohl dies keinen gültigen Wert darstellt. (Andernfalls wird es für den Benutzer ärgerlich, wenn er beispielsweise “1” in “2” ändern möchte.) Ebenso möchten Sie wahrscheinlich Dinge wie “-” und “.” usw. zulassen.

Hier ist ein Beispiel. Der Filter sollte die ihm übergebene Änderung bei Bedarf modifizieren und kann zurückkehren null einer Änderung vollständig widersprechen. Dieses Beispiel überprüft einfach, ob der Text einen gültigen Bearbeitungsstatus darstellt, und gibt die Änderung unverändert zurück, wenn dies der Fall ist, andernfalls wird dagegen ein Veto eingelegt. Der Formatierer muss jeden Text verarbeiten, der vom Filter zugelassen wird, und ihn in einen Double konvertieren. Hier wird alles, was unvollständig ist, einfach als Null dargestellt.

Pattern validEditingState = Pattern.compile("-?(([1-9][0-9]*)|0)?(\\.[0-9]*)?");

UnaryOperator<TextFormatter.Change> filter = c -> {
    String text = c.getControlNewText();
    if (validEditingState.matcher(text).matches()) {
        return c ;
    } else {
        return null ;
    }
};

StringConverter<Double> converter = new StringConverter<Double>() {

    @Override
    public Double fromString(String s) {
        if (s.isEmpty() || "-".equals(s) || ".".equals(s) || "-.".equals(s)) {
            return 0.0 ;
        } else {
            return Double.valueOf(s);
        }
    }


    @Override
    public String toString(Double d) {
        return d.toString();
    }
};

TextFormatter<Double> textFormatter = new TextFormatter<>(converter, 0.0, filter);
TextField textField = new TextField();
textField.setTextFormatter(textFormatter);

Sie können den regulären Ausdruck bei Bedarf komplexer machen, z. B. um das Gruppieren von Zeichen zu unterstützen ("1,000.0"), Lokalisierung ("1.003,14159" wenn das für das Gebietsschema angemessen ist) oder Darstellungen in wissenschaftlicher Notation ("6.022E23"usw.) und um Mindest- oder Höchstwerte zu erzwingen usw. Sie können sogar Dinge tun, wie z. B. die Änderung ändern, sodass, wenn der Benutzer a - An einer beliebigen Stelle im Text wird nur das Vorzeichen der Zahl umgedreht. (Siehe die TextFormatter.Change Dokumentation für diese Art von Funktionalität.)

Beachten Sie, dass Sie den Double-Wert (wie vom Konverter bereitgestellt) direkt vom Formatierer erhalten und festlegen können, der über eine verfügt ObjectProperty<Double> valueProperty(). Sie können also Dinge tun wie

// update text field:
double value = ... ;
textFormatter.setValue(value);

// listen for changes in double value represented in text field
// Listener will be invoked when the user commits an edit:

textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
    System.out.println("User entered value: "+newValue.doubleValue());
});

Hier ist ein SSCCE. Das zweite Textfeld ist nur da, damit Sie den Effekt sehen können, wenn der Fokus auf ein anderes Steuerelement verschoben wird (es “übergibt” den Wert und ruft den Listener im Textformatierer auf, wenn sich der Wert geändert hat; Ähnliches passiert, wenn der Benutzer drückt die Eingabetaste).

import java.util.function.UnaryOperator;
import java.util.regex.Pattern;

import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;

public class NumericTextField extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Pattern validEditingState = Pattern.compile("-?(([1-9][0-9]*)|0)?(\\.[0-9]*)?");

        UnaryOperator<TextFormatter.Change> filter = c -> {
            String text = c.getControlNewText();
            if (validEditingState.matcher(text).matches()) {
                return c ;
            } else {
                return null ;
            }
        };

        StringConverter<Double> converter = new StringConverter<Double>() {

            @Override
            public Double fromString(String s) {
                if (s.isEmpty() || "-".equals(s) || ".".equals(s) || "-.".equals(s)) {
                    return 0.0 ;
                } else {
                    return Double.valueOf(s);
                }
            }


            @Override
            public String toString(Double d) {
                return d.toString();
            }
        };

        TextFormatter<Double> textFormatter = new TextFormatter<>(converter, 0.0, filter);
        TextField textField = new TextField();
        textField.setTextFormatter(textFormatter);

        textFormatter.valueProperty().addListener((ObservableValue<? extends Double> obs, Double oldValue, Double newValue) -> {
            System.out.println("User entered value: "+newValue.doubleValue());
        });

        VBox root = new VBox(5, textField, new TextField());
        root.setAlignment(Pos.CENTER);
        primaryStage.setScene(new Scene(root, 250, 250));
        primaryStage.show();
    }

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

}

  • Führen Sie keinen manuellen Musterabgleich durch, da dies bei den meisten nicht-amerikanischen/englischen Gebietsschemas fehlschlägt.

    – Kleopatra

    4. August 2019 um 11:28 Uhr

Sie könnten Double.parseDouble verwenden Doppeltes Javadoc. Dies übernimmt das Parsen für Sie, anstatt Ihre eigene doppelte Validierung zu schreiben. Sie können dann nach einer Zahlenformat-Ausnahme oder einer Nullzeiger-Ausnahme suchen.

    try
    {
        Double.parseDouble(newValue);
        // Valid double
    }
    catch (NullPointerException | NumberFormatException ex)
    {
        // Not valid double
    }

  • Danke für deine Antwort, aber ich versuche es Gewalt den Benutzer, nur doppelte Werte einzugeben. Ich möchte die Verwaltung ungültiger Eingaben vermeiden.

    – Robb1

    31. August 2017 um 10:02 Uhr

  • Die einzige Möglichkeit, einen Benutzer zur Eingabe eines Doppels zu zwingen, besteht darin, eine ungültige Eingabe ohnehin zu erkennen. Es gibt keinen Unterschied zwischen einem if bei einer Regex-Übereinstimmung oder dem Ziehen des obigen Codes in eine testDouble-Methode, die true oder false zurückgibt.

    – Matthäus Clark

    31. August 2017 um 10:11 Uhr

  • “Sie können einen Benutzer nur dazu zwingen, eine doppelte Eingabe zu machen, indem Sie trotzdem eine ungültige Eingabe erkennen.” Nicht wahr. Sie können die verschiedenen überschreiben insert Methoden, zum Beispiel, oder verwenden Sie a TextFormatter.

    – James_D

    31. August 2017 um 12:05 Uhr

  • Es ist auch eine sehr schlechte Praxis, die Ausnahmebehandlung für die logische Steuerung zu verwenden. Ausnahmen sollten für außergewöhnliche Bedingungen gelten, nicht etwas, von dem Sie erwarten, dass es im normalen Lauf der Dinge passiert.

    – James_D

    31. August 2017 um 12:39 Uhr

  • Die Verwendung von Double.parse ist so schlecht wie jeder manuelle Musterabgleich: Keiner ist gebietsschemaabhängig. Zumindest müssen Sie ein NumberFormat … in einem TextFormatter verwenden, wie von @James_D vorgeschlagen

    – Kleopatra

    4. August 2019 um 11:26 Uhr

Benutzer-Avatar
D.Cosentino

Sie können den Java-Regex für Doubles verwenden und Zeichen, die keine Zahlen sind, durch Zahlen ersetzen. In müssen Sie die zulässige Anzahl von Stellen im ganzzahligen Teil angeben, zum Beispiel 1,10.

textField.textProperty().addListener(new ChangeListener<String>() {
    @Override
    public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
        if (!newValue.matches("[0-9]{<range>}(\\.[0-9]*)?")) {
            textField.setText(newValue.replaceAll("[^\\d.]", ""));
            StringBuilder aus = new StringBuilder(newValue);
            boolean firstPointFound = false;
            for (int i = 0; i < aus.length(); i++){
                if(aus.charAt(i) == '.') {
                    if(!firstPointFound)
                        firstPointFound = true;
                    else
                        aus.deleteCharAt(i);
                }
            }
            newValue = aus.toString();
        }
    }
});

  • Danke für deine Antwort, aber so versuche ich das einzugeben 99.99. beim zweiten . Ich erhalte eine Fehlermeldung (und außerdem wird die Eingabe geändert in 9999). Irgendeine Idee, wie man das beheben kann?

    – Robb1

    31. August 2017 um 10:01 Uhr

1014220cookie-checkWie erzwinge ich eine doppelte Eingabe in einem TextField in JavaFX?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy