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]", ""));
}
}
});
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:
- Definir un “Filtro”, que puede vetar o modificar cualquier cambio realizado en el
TextField
el texto de
- 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);
}
}
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
}
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();
}
}
});
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