Saltar al contenido

¿Cómo implementar un NumberField en javaFX 2.0?

Solución:

Actualización 27 de mayo de 2016

Java 8u40 introdujo la clase TextFormatter, que es la forma recomendada de lograr esta funcionalidad (aunque la solución proporcionada en esta respuesta seguirá funcionando). Para obtener más información, consulte la respuesta de Uwe, la respuesta de Hassan y otras respuestas que mencionan TextFormatter a la siguiente pregunta:

  • ¿Cuál es la forma recomendada de hacer un TextField numérico en JavaFX?

También existe esta solución de otra respuesta a esta pregunta que no he probado, pero se ve bien y se eliminó un moderador de StackOverflow:

TextField numberField = new TextField();
numberField.setTextFormatter(new TextFormatter<>(new NumberStringConverter()));

El código anterior omite el filtro UnaryOperator para TextFormatter, que generalmente también requiere (de lo contrario, el campo no mostrará la entrada del usuario restringida solo al valor formateado, solo le permitirá monitorear el valor sin formato a través de la propiedad de valor de los formateadores de texto ). Para extender la solución para usar un filtro, se podría usar un código como el que se muestra a continuación:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;

import java.text.ParsePosition;
import java.util.function.UnaryOperator;

public class NumberConverterFieldTest extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        TextField numberField = new TextField();
        NumberStringFilteredConverter converter = new NumberStringFilteredConverter();
        final TextFormatter<Number> formatter = new TextFormatter<>(
                converter,
                0,
                converter.getFilter()
        );

        numberField.setTextFormatter(formatter);

        formatter.valueProperty().addListener((observable, oldValue, newValue) ->
                System.out.println(newValue)
        );

        stage.setScene(new Scene(numberField));
        stage.show();
    }

    class NumberStringFilteredConverter extends NumberStringConverter {
        // Note, if needed you can add in appropriate constructors 
        // here to set locale, pattern matching or an explicit
        // type of NumberFormat.
        // 
        // For more information on format control, see 
        //    the NumberStringConverter constructors
        //    DecimalFormat class 
        //    NumberFormat static methods for examples.
        // This solution can instead extend other NumberStringConverters if needed
        //    e.g. CurrencyStringConverter or PercentageStringConverter.

        public UnaryOperator<TextFormatter.Change> getFilter() {
            return change -> {
                String newText = change.getControlNewText();
                if (newText.isEmpty()) {
                    return change;
                }

                ParsePosition parsePosition = new ParsePosition( 0 );
                Object object = getNumberFormat().parse( newText, parsePosition );
                if ( object == null || parsePosition.getIndex() < newText.length()) {
                    return null;
                } else {
                    return change;
                }
            };
        }
    }
}

Al ejecutar el ejemplo anterior, edite el campo de entrada y presione la tecla Intro para ver el valor actualizado (el valor actualizado se envía a System.out cuando se cambia).

Para ver un tutorial, consulte:

  • Artículo de JavaWorld TextFormatter (cuidado con los molestos anuncios emergentes).

Esta es la misma solución a la que hace referencia Urs, sin embargo, la coloqué en un programa completamente ejecutable para proporcionar un ejemplo en contexto y modifiqué la expresión regular (agregando * al final) para que copiar y pegar funcione y no tenga el problema al que se refiere Uluk. La solución parece bastante simple y probablemente será suficiente para la mayoría de los propósitos:

import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class NumericTextFieldTest extends Application {
  public static void main(String[] args) { launch(args); }

  @Override public void start(Stage stage) {
    TextField numberField = new TextField() {
      @Override public void replaceText(int start, int end, String text) {
        if (text.matches("[0-9]*")) {
          super.replaceText(start, end, text);
        }
      }

      @Override public void replaceSelection(String text) {
        if (text.matches("[0-9]*")) {
          super.replaceSelection(text);
        }
      }
    };

    stage.setScene(new Scene(numberField));
    stage.show();
  }
}

Soluciones alternativas

También podría estar interesado en mi solución alternativa en el ejemplo de JavaFX de vincular un valor de control deslizante a un TextField editable. En esa solución, extiendo TextField para exponer un IntegerProperty en el campo con fines de enlace simple. La solución alternativa es similar a la descrita por el póster original en su pregunta actualizada (es decir, se agrega un filtro de eventos para restringir los datos de entrada de los eventos clave), pero además se agrega un ChangeListener en la propiedad de texto de TextField para garantizar que se copien y peguen los valores solo se aceptan si son numéricos.

¿Hay otras soluciones a esta pregunta en el hilo del foro JavaFX Numeric Textfield en JavaFX 2.0? que incluye una referencia a los campos numéricos de los controles FXExperience.

Hay un consejo en FXExperience que trata un problema así. Parafraseando, extiendes el TextField y anular el replaceText() y replaceSelection() métodos, filtrando toda la entrada que no sea un número.

Una vez implementados, ambos métodos deben seguir esta plantilla:

if (!newText.matches("[0-9]")) {
    super.call(allParams)
}

Encontré una solución. 🙂

public class NumFieldFX extends TextField {
   public NumFieldFX() {
      this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
         public void handle( KeyEvent t ) {
            char ar[] = t.getCharacter().toCharArray();
            char ch = ar[t.getCharacter().toCharArray().length - 1];
            if (!(ch >= '0' && ch <= '9')) {
               System.out.println("The char you entered is not a number");
               t.consume();
            }
         }
      });
   }
}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *