Eu preciso restringir a entrada em um TextField para inteiros. Algum conselho?
Eu preciso restringir a entrada em um TextField para inteiros. Algum conselho?
Respostas:
Tópico muito antigo, mas parece mais organizado e remove os caracteres não numéricos se colado.
// force the field to be numeric only
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]", ""));
}
}
});
\\D+
(ou apenas \\D
) em vez de [^\\d]
, se quiser salvar alguns caracteres.
Integer.parseInt(newValue)
e usar try
e catch
localizar um erroNumberFormatException
Atualização de abril de 2016
Essa resposta foi criada há alguns anos e a resposta original está obsoleta agora.
Desde Java 8u40, Java tem um TextFormatter que geralmente é melhor para forçar a entrada de formatos específicos, como numéricos, em TextFields JavaFX:
Veja também outras respostas a esta pergunta que mencionam especificamente TextFormatter.
Resposta Original
Existem alguns exemplos disso nesta essência , eu dupliquei um dos exemplos abaixo:
// helper text field subclass which restricts text input to a given range of natural int numbers
// and exposes the current numeric int value of the edit box as a value property.
class IntField extends TextField {
final private IntegerProperty value;
final private int minValue;
final private int maxValue;
// expose an integer value property for the text field.
public int getValue() { return value.getValue(); }
public void setValue(int newValue) { value.setValue(newValue); }
public IntegerProperty valueProperty() { return value; }
IntField(int minValue, int maxValue, int initialValue) {
if (minValue > maxValue)
throw new IllegalArgumentException(
"IntField min value " + minValue + " greater than max value " + maxValue
);
if (maxValue < minValue)
throw new IllegalArgumentException(
"IntField max value " + minValue + " less than min value " + maxValue
);
if (!((minValue <= initialValue) && (initialValue <= maxValue)))
throw new IllegalArgumentException(
"IntField initialValue " + initialValue + " not between " + minValue + " and " + maxValue
);
// initialize the field values.
this.minValue = minValue;
this.maxValue = maxValue;
value = new SimpleIntegerProperty(initialValue);
setText(initialValue + "");
final IntField intField = this;
// make sure the value property is clamped to the required range
// and update the field's text to be in sync with the value.
value.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
if (newValue == null) {
intField.setText("");
} else {
if (newValue.intValue() < intField.minValue) {
value.setValue(intField.minValue);
return;
}
if (newValue.intValue() > intField.maxValue) {
value.setValue(intField.maxValue);
return;
}
if (newValue.intValue() == 0 && (textProperty().get() == null || "".equals(textProperty().get()))) {
// no action required, text property is already blank, we don't need to set it to 0.
} else {
intField.setText(newValue.toString());
}
}
}
});
// restrict key input to numerals.
this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() {
@Override public void handle(KeyEvent keyEvent) {
if(intField.minValue<0) {
if (!"-0123456789".contains(keyEvent.getCharacter())) {
keyEvent.consume();
}
}
else {
if (!"0123456789".contains(keyEvent.getCharacter())) {
keyEvent.consume();
}
}
}
});
// ensure any entered values lie inside the required range.
this.textProperty().addListener(new ChangeListener<String>() {
@Override public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
if (newValue == null || "".equals(newValue) || (intField.minValue<0 && "-".equals(newValue))) {
value.setValue(0);
return;
}
final int intValue = Integer.parseInt(newValue);
if (intField.minValue > intValue || intValue > intField.maxValue) {
textProperty().setValue(oldValue);
}
value.set(Integer.parseInt(textProperty().get()));
}
});
}
}
Eu sei que este é um tópico bastante antigo, mas para futuros leitores, aqui está outra solução que achei bastante intuitiva:
public class NumberTextField extends TextField
{
@Override
public void replaceText(int start, int end, String text)
{
if (validate(text))
{
super.replaceText(start, end, text);
}
}
@Override
public void replaceSelection(String text)
{
if (validate(text))
{
super.replaceSelection(text);
}
}
private boolean validate(String text)
{
return text.matches("[0-9]*");
}
}
Edit: Obrigado none_ e SCBoy por suas sugestões de melhorias.
text.matches("\\d+");
e não consigo excluir nenhum caractere no campo de texto
A partir do JavaFX 8u40, você pode definir um objeto TextFormatter em um campo de texto:
UnaryOperator<Change> filter = change -> {
String text = change.getText();
if (text.matches("[0-9]*")) {
return change;
}
return null;
};
TextFormatter<String> textFormatter = new TextFormatter<>(filter);
fieldNport = new TextField();
fieldNport.setTextFormatter(textFormatter);
Isso evita a criação de subclasses e eventos de alteração duplicados que você obterá ao adicionar um ouvinte de alteração à propriedade de texto e modificar o texto nesse ouvinte.
O TextInput
tem um TextFormatter
que pode ser usado para formatar, converter e limitar os tipos de texto que podem ser inseridos.
O TextFormatter
possui um filtro que pode ser usado para rejeitar entradas. Precisamos definir isso para rejeitar qualquer coisa que não seja um número inteiro válido. Ele também tem um conversor que precisamos definir para converter o valor da string em um valor inteiro que podemos ligar mais tarde.
Vamos criar um filtro reutilizável:
public class IntegerFilter implements UnaryOperator<TextFormatter.Change> {
private final static Pattern DIGIT_PATTERN = Pattern.compile("\\d*");
@Override
public Change apply(TextFormatter.Change aT) {
return DIGIT_PATTERN.matcher(aT.getText()).matches() ? aT : null;
}
}
O filtro pode fazer uma das três coisas: pode retornar a alteração sem modificações para aceitá-la como está, pode alterar a alteração de alguma forma que considere adequada ou pode retornar null
para rejeitar a alteração totalmente.
Usaremos o padrão IntegerStringConverter
como conversor.
Juntando tudo, temos:
TextField textField = ...;
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(), // Standard converter form JavaFX
defaultValue,
new IntegerFilter());
formatter.valueProperty().bindBidirectional(myIntegerProperty);
textField.setTextFormatter(formatter);
Se você quiser não precisa de um filtro reutilizável, pode fazer esta linha sofisticada:
TextFormatter<Integer> formatter = new TextFormatter<>(
new IntegerStringConverter(),
defaultValue,
c -> Pattern.matches("\\d*", c.getText()) ? c : null );
Não gosto de exceções, por isso usei a matches
função String-Class
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (newValue.matches("\\d*")) {
int value = Integer.parseInt(newValue);
} else {
text.setText(oldValue);
}
}
});
textField.positionCaret(textField.getLength());
if (newValue.matches("\\d*") && newValue.getText().length < 5)
se quiser limitar a entrada a 4 dígitos neste caso.
A partir do Java SE 8u40 , para tal necessidade você pode usar um " inteiro " Spinner
permitindo selecionar com segurança um inteiro válido usando as teclas de seta para cima / seta para baixo do teclado ou os botões de seta para cima / seta para baixo fornecidos.
Você também pode definir um mínimo , um máximo e um valor inicial para limitar os valores permitidos e uma quantidade para aumentar ou diminuir, por etapa.
Por exemplo
// Creates an integer spinner with 1 as min, 10 as max and 2 as initial value
Spinner<Integer> spinner1 = new Spinner<>(1, 10, 2);
// Creates an integer spinner with 0 as min, 100 as max and 10 as initial
// value and 10 as amount to increment or decrement by, per step
Spinner<Integer> spinner2 = new Spinner<>(0, 100, 10, 10);
Exemplo de resultado com um spinner " inteiro " e um spinner " duplo "
Um controle giratório é um controle de campo de texto de linha única que permite ao usuário selecionar um número ou valor de objeto em uma sequência ordenada de tais valores. Spinners geralmente fornecem um par de pequenos botões de seta para percorrer os elementos da sequência. As teclas de seta para cima / seta para baixo do teclado também percorrem os elementos. O usuário também pode digitar um valor (legal) diretamente no botão giratório. Embora as caixas de combinação forneçam funcionalidade semelhante, os spinners às vezes são preferidos porque não requerem uma lista suspensa que pode obscurecer dados importantes e também porque permitem recursos como quebra automática do valor máximo de volta ao valor mínimo (por exemplo, do maior número inteiro positivo a 0).
Mais detalhes sobre o controle Spinner
TextField text = new TextField();
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try {
Integer.parseInt(newValue);
if (newValue.endsWith("f") || newValue.endsWith("d")) {
manualPriceInput.setText(newValue.substring(0, newValue.length()-1));
}
} catch (ParseException e) {
text.setText(oldValue);
}
}
});
A if
cláusula é importante para lidar com entradas como 0,5d ou 0,7f, que são analisadas corretamente por Int.parseInt (), mas não devem aparecer no campo de texto.
Tente este código simples, ele fará o trabalho.
DecimalFormat format = new DecimalFormat( "#.0" );
TextField field = new TextField();
field.setTextFormatter( new TextFormatter<>(c ->
{
if ( c.getControlNewText().isEmpty() )
{
return c;
}
ParsePosition parsePosition = new ParsePosition( 0 );
Object object = format.parse( c.getControlNewText(), parsePosition );
if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
{
return null;
}
else
{
return c;
}
}));
Se você deseja aplicar o mesmo ouvinte a mais de um TextField, aqui está a solução mais simples:
TextField txtMinPrice, txtMaxPrice = new TextField();
ChangeListener<String> forceNumberListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*"))
((StringProperty) observable).set(oldValue);
};
txtMinPrice.textProperty().addListener(forceNumberListener);
txtMaxPrice.textProperty().addListener(forceNumberListener);
Este funcionou para mim.
public void RestrictNumbersOnly(TextField tf){
tf.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue,
String newValue) {
if (!newValue.matches("|[-\\+]?|[-\\+]?\\d+\\.?|[-\\+]?\\d+\\.?\\d+")){
tf.setText(oldValue);
}
}
});
}
Quero ajudar com minha ideia combinando a resposta de Evan Knowles com o TextFormatter
JavaFX 8
textField.setTextFormatter(new TextFormatter<>(c -> {
if (!c.getControlNewText().matches("\\d*"))
return null;
else
return c;
}
));
então boa sorte;) mantenha a calma e codifique java
Aqui está uma classe simples que lida com algumas validações básicas em TextField
, usando TextFormatter
introduzido no JavaFX 8u40
EDITAR:
(Código adicionado em relação ao comentário de Floern)
import java.text.DecimalFormatSymbols;
import java.util.regex.Pattern;
import javafx.beans.NamedArg;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
public class TextFieldValidator {
private static final String CURRENCY_SYMBOL = DecimalFormatSymbols.getInstance().getCurrencySymbol();
private static final char DECIMAL_SEPARATOR = DecimalFormatSymbols.getInstance().getDecimalSeparator();
private final Pattern INPUT_PATTERN;
public TextFieldValidator(@NamedArg("modus") ValidationModus modus, @NamedArg("countOf") int countOf) {
this(modus.createPattern(countOf));
}
public TextFieldValidator(@NamedArg("regex") String regex) {
this(Pattern.compile(regex));
}
public TextFieldValidator(Pattern inputPattern) {
INPUT_PATTERN = inputPattern;
}
public static TextFieldValidator maxFractionDigits(int countOf) {
return new TextFieldValidator(maxFractionPattern(countOf));
}
public static TextFieldValidator maxIntegers(int countOf) {
return new TextFieldValidator(maxIntegerPattern(countOf));
}
public static TextFieldValidator integersOnly() {
return new TextFieldValidator(integersOnlyPattern());
}
public TextFormatter<Object> getFormatter() {
return new TextFormatter<>(this::validateChange);
}
private Change validateChange(Change c) {
if (validate(c.getControlNewText())) {
return c;
}
return null;
}
public boolean validate(String input) {
return INPUT_PATTERN.matcher(input).matches();
}
private static Pattern maxFractionPattern(int countOf) {
return Pattern.compile("\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?");
}
private static Pattern maxCurrencyFractionPattern(int countOf) {
return Pattern.compile("^\\" + CURRENCY_SYMBOL + "?\\s?\\d*(\\" + DECIMAL_SEPARATOR + "\\d{0," + countOf + "})?\\s?\\" +
CURRENCY_SYMBOL + "?");
}
private static Pattern maxIntegerPattern(int countOf) {
return Pattern.compile("\\d{0," + countOf + "}");
}
private static Pattern integersOnlyPattern() {
return Pattern.compile("\\d*");
}
public enum ValidationModus {
MAX_CURRENCY_FRACTION_DIGITS {
@Override
public Pattern createPattern(int countOf) {
return maxCurrencyFractionPattern(countOf);
}
},
MAX_FRACTION_DIGITS {
@Override
public Pattern createPattern(int countOf) {
return maxFractionPattern(countOf);
}
},
MAX_INTEGERS {
@Override
public Pattern createPattern(int countOf) {
return maxIntegerPattern(countOf);
}
},
INTEGERS_ONLY {
@Override
public Pattern createPattern(int countOf) {
return integersOnlyPattern();
}
};
public abstract Pattern createPattern(int countOf);
}
}
Você pode usá-lo assim:
textField.setTextFormatter(new TextFieldValidator(ValidationModus.INTEGERS_ONLY).getFormatter());
ou você pode instanciá-lo em um arquivo fxml e aplicá-lo a um customTextField com as propriedades correspondentes.
app.fxml:
<fx:define>
<TextFieldValidator fx:id="validator" modus="INTEGERS_ONLY"/>
</fx:define>
CustomTextField.class:
public class CustomTextField {
private TextField textField;
public CustomTextField(@NamedArg("validator") TextFieldValidator validator) {
this();
textField.setTextFormatter(validator.getFormatter());
}
}
Isso é o que eu uso:
private TextField textField;
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
}
});
O mesmo na notação lambda seria:
private TextField textField;
textField.textProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue.matches("[0-9]*")){
textField.setText(oldValue);
}
});
Este método permite que TextField termine todo o processamento (copiar / colar / desfazer seguro). Não requer estender classes e permite que você decida o que fazer com o novo texto após cada mudança (para colocá-lo na lógica, ou voltar ao valor anterior, ou mesmo modificá-lo).
// fired by every text property change
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
// Your validation rules, anything you like
// (! note 1 !) make sure that empty string (newValue.equals(""))
// or initial text is always valid
// to prevent inifinity cycle
// do whatever you want with newValue
// If newValue is not valid for your rules
((StringProperty)observable).setValue(oldValue);
// (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
// to anything in your code. TextProperty implementation
// of StringProperty in TextFieldControl
// will throw RuntimeException in this case on setValue(string) call.
// Or catch and handle this exception.
// If you want to change something in text
// When it is valid for you with some changes that can be automated.
// For example change it to upper case
((StringProperty)observable).setValue(newValue.toUpperCase());
}
);
Para o seu caso, basta adicionar essa lógica dentro. Funciona perfeitamente.
if (newValue.equals("")) return;
try {
Integer i = Integer.valueOf(newValue);
// do what you want with this i
} catch (Exception e) {
((StringProperty)observable).setValue(oldValue);
}
Mmmm. Eu tive esse problema semanas atrás. Como a API não fornece um controle para isso,
você pode usar o seu próprio. Eu usei algo como:
public class IntegerBox extends TextBox {
public-init var value : Integer = 0;
protected function apply() {
try {
value = Integer.parseInt(text);
} catch (e : NumberFormatException) {}
text = "{value}";
}
override var focused = false on replace {apply()};
override var action = function () {apply()}
}
É usado da mesma forma que um normal TextBox
,
mas também tem um value
atributo que armazena o inteiro inserido.
Quando o controle perde o foco, ele valida o valor e o reverte (se não for válido).
este código Faça seu textField Aceitar apenas o número
textField.lengthProperty().addListener((observable, oldValue, newValue) -> {
if(newValue.intValue() > oldValue.intValue()){
char c = textField.getText().charAt(oldValue.intValue());
/** Check if the new character is the number or other's */
if( c > '9' || c < '0'){
/** if it's not number then just setText to previous one */
textField.setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
});
Este código funciona bem para mim, mesmo se você tentar copiar / colar.
myTextField.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
myTextField.setText(oldValue);
}
});
Em atualizações recentes do JavaFX, você deve definir um novo texto no método Platform.runLater assim:
private void set_normal_number(TextField textField, String oldValue, String newValue) {
try {
int p = textField.getCaretPosition();
if (!newValue.matches("\\d*")) {
Platform.runLater(() -> {
textField.setText(newValue.replaceAll("[^\\d]", ""));
textField.positionCaret(p);
});
}
} catch (Exception e) {
}
}
É uma boa ideia definir a posição do cursor também.
Platform.runLater
é necessário?
Eu gostaria de melhorar a resposta de Evan Knowles: https://stackoverflow.com/a/30796829/2628125
No meu caso, tive aula com manipuladores para a parte do componente de interface do usuário. Inicialização:
this.dataText.textProperty().addListener((observable, oldValue, newValue) -> this.numericSanitization(observable, oldValue, newValue));
E o método numbericSanitization:
private synchronized void numericSanitization(ObservableValue<? extends String> observable, String oldValue, String newValue) {
final String allowedPattern = "\\d*";
if (!newValue.matches(allowedPattern)) {
this.dataText.setText(oldValue);
}
}
Palavra-chave - sincronizada palavra- é adicionada para evitar um possível problema de bloqueio de renderização em javafx se setText for chamado antes que o antigo termine a execução. É fácil de reproduzir se você começar a digitar os caracteres errados muito rápido.
Outra vantagem é que você mantém apenas um padrão para corresponder e apenas faz o rollback. É melhor porque você pode abstrair facilmente a solução para diferentes padrões de sanitização.
rate_text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
String s="";
for(char c : newValue.toCharArray()){
if(((int)c >= 48 && (int)c <= 57 || (int)c == 46)){
s+=c;
}
}
rate_text.setText(s);
}
});
Isso funciona bem, pois permite que você insira apenas o valor inteiro e o valor decimal (com código ASCII 46).