¿Cómo forzar una entrada doble en un TextField en JavaFX?

7 minutos de lectura

avatar de usuario
Robb1

Cómo asegurarse de que un el usuario ingresa solo valores dobles en un TextField dado?

Encontré una solución para números enteros, pero no puedo usarla para dobles. ¿Qué debo escribir en lugar de la "\\d*" y "[^\\d]" para que funcione para dobles?

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

  • No utilice un oyente; otros observadores de la propiedad de texto verán la entrada no válida antes de que se revierta. Utilizar una TextFormatter en lugar de.

    – James_D

    31 de agosto de 2017 a las 12:04

avatar de usuario
James_D

El enfoque de usar un oyente y volver a valores válidos si el usuario ingresa valores no válidos funcionará, pero puede crear problemas si tiene otros oyentes en el campo de texto. textProperty. Esos oyentes observarán los valores no válidos así como los válidos, por lo que deben saber filtrar cualquier valor no válido.

Un mejor enfoque es utilizar un TextFormatter. los TextFormatter puede hacer dos cosas:

  1. Definir un “Filtro”, que puede vetar o modificar cualquier cambio realizado en el TextFieldel texto de
  2. Defina un “convertidor”, que define cómo convertir el texto hacia y desde valores de cualquier tipo específico (p. ej. Double) en tu caso.

Definir el filtro apropiado puede ser complicado: desea permitir cualquier edición razonable por parte del usuario. Esto significa que el texto puede estar en un estado no válido mientras el usuario lo está editando; por ejemplo, probablemente desee permitir que el campo de texto esté completamente vacío, aunque eso no represente un valor válido. (De lo contrario, se vuelve molesto para el usuario si, por ejemplo, quiere cambiar “1” a “2”). De manera similar, probablemente desee permitir cosas como “-” y “.”, etc.

Aquí hay un ejemplo. El filtro debe modificar el cambio que se le pasa, si es necesario, y puede devolver null vetar por completo un cambio. Este ejemplo simplemente verifica si el texto representa un estado de edición válido y devuelve el cambio sin modificar si lo hace, vetándolo de lo contrario. El formateador necesita lidiar con cualquier texto permitido por el filtro y convertirlo en un doble. Aquí todo lo que está incompleto se representa simplemente como cero.

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

Puede hacer que la expresión regular sea más compleja si es necesario, por ejemplo, para permitir la agrupación de caracteres ("1,000.0"), localización ("1.003,14159" si eso es apropiado para el lugar), o representaciones similares a la notación científica ("6.022E23"etc), y para hacer cumplir valores mínimos o máximos, etc. Incluso puede hacer cosas como modificar el cambio, de modo que si el usuario escribe un - en cualquier parte del texto, simplemente cambia el signo del número. (Referirse a TextFormatter.Change documentación para ese tipo de funcionalidad.)

Tenga en cuenta que puede obtener y establecer el valor doble (como lo proporciona el convertidor) directamente desde el formateador, que tiene un ObjectProperty<Double> valueProperty(). Entonces puedes hacer cosas como

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

Aquí hay un SSCCE. El segundo campo de texto está ahí para que pueda ver el efecto de mover el foco a un control diferente (“confirma” el valor e invoca al oyente en el formateador de texto, si el valor ha cambiado; sucede algo similar si el usuario presiona enter).

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

}

  • no haga ninguna coincidencia manual de patrones, se romperá en la mayoría de las configuraciones regionales que no sean de EE. UU./Inglés.

    – cleopatra

    4 de agosto de 2019 a las 11:28

Podrías hacer uso de Double.parseDouble Javadoc doble. Esto hará el análisis por usted, en lugar de escribir su propia validación doble. A continuación, puede comprobar si hay una excepción de formato de número o una excepción de puntero nulo.

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

  • Gracias por tu respuesta, pero estoy tratando de fuerza el usuario para ingresar solo valores dobles. Me gustaría evitar la gestión de entradas no válidas.

    – Robb1

    31 de agosto de 2017 a las 10:02

  • La única forma en que puede obligar a un usuario a ingresar un doble es detectar una entrada no válida de todos modos. No hay diferencia entre hacer un if en una coincidencia de expresiones regulares o extraer el código anterior en un método testDouble que devuelve verdadero o falso.

    – Mateo Clark

    31 de agosto de 2017 a las 10:11

  • “La única forma en que puede obligar a un usuario a ingresar un doble es detectar una entrada no válida de todos modos”. No es verdad. Puede anular los diversos insert métodos, por ejemplo, o utilizar un TextFormatter.

    – James_D

    31 de agosto de 2017 a las 12:05

  • También es una muy mala práctica usar el manejo de excepciones para el control lógico. Las excepciones deben ser para condiciones excepcionales, no algo que espera que suceda en el transcurso normal de los eventos.

    – James_D

    31 de agosto de 2017 a las 12:39

  • usar Double.parse es tan malo como cualquier patrón de coincidencia manual: ninguno es consciente de la configuración regional. Como mínimo, debe usar un NumberFormat … dentro de un TextFormatter, como lo sugiere @James_D

    – cleopatra

    4 de agosto de 2019 a las 11:26

avatar de usuario
D. Cosentino

Puede usar la expresión regular de Java en dobles y reemplazar caracteres que no sean números con números. En tiene que especificar el número de dígitos permitidos en la parte entera, por ejemplo 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();
        }
    }
});

  • Gracias por tu respuesta, pero de tal manera si trato de ingresar 99.99. en el segundo . Recibo un error (y además la entrada se cambia a 9999). Alguna idea de como arreglar esto?

    – Robb1

    31 de agosto de 2017 a las 10:01

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad