ti-enxame.com

Como implementar um NumberField em javaFX 2.0?

Eu preciso inserir um campo numérico na minha interface do usuário. Portanto, preciso verificar os principais eventos em um campo de texto para verificar se o caractere de entrada é um número. Eu criei uma classe estendendo TextField. Se houver um método na classe TextField que lide com keyEvents, posso simplesmente substituir esse método por ternos para o campo numérico. Alguma ideia?

Obrigado

18
Anuruddha

Encontrei uma solução. :)

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

Atualização em 27 de maio de 2016

O Java 8u40 introduziu a classe TextFormatter que é a maneira recomendada de realizar essa funcionalidade (embora a solução fornecida nesta resposta ainda funcione). Para mais informações, veja resposta de Uwe , resposta de Hassan e outras respostas mencionando TextFormatter à seguinte pergunta:

Há também esta solução de outra resposta a esta pergunta que eu não tentei, mas parece ser bom e um moderador StackOverflow foi excluído:

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

O código acima perde o filtro UnaryOperator para o TextFormatter, que você normalmente também exige (caso contrário, o campo não exibirá a entrada restrita do usuário somente ao valor formatado, apenas permitirá monitorar o valor não formatado através da propriedade value dos formatadores de texto ). Para estender a solução para usar um filtro, o código abaixo pode ser usado:

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

Ao executar o exemplo acima, edite o campo de entrada e pressione a tecla enter para ver o valor atualizado (o valor atualizado é enviado para System.out quando alterado).

Para um tutorial, veja:


Esta é a mesma solução que Urs faz referência, no entanto eu apenas coloquei em um programa totalmente executável para fornecer um exemplo no contexto e modifiquei a expressão regular (adicionando * no final) para que copiar e colar funcionem e não tenha o questão Uluk refere-se a. A solução parece bastante simples e provavelmente será suficiente para a maioria das finalidades:

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

Soluções alternativas

Você também pode estar interessado em minha solução alternativa em exemplo do JavaFX de vincular um valor de controle deslizante a um TextField editável . Nessa solução, eu estendo o TextField para expor um IntegerProperty no campo para fins de vinculação simples. A solução alternativa é semelhante à descrita pelo pôster original em sua pergunta atualizada (ou seja, um filtro de eventos é adicionado para restringir dados de entrada de eventos-chave), mas adicionalmente um ChangeListener é adicionado na propriedade de texto do TextField para garantir que os valores copiados e colados só são aceitos se forem numéricos.

Existem algumas outras soluções para essa questão no tópico do fórum JavaFX Textile numérico no JavaFX 2.0? Que inclui uma referência aos campos numéricos dos controles FXExperience .

14
jewelsea

Há uma dica no FXExperience que lida com um problema como esse. Parafraseando, você estende o TextField e substitui os métodos replaceText() e replaceSelection(), filtrando toda a entrada que não é um número.

Depois de implementados, os dois métodos devem seguir este modelo:

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

Aqui está o campo CustomText que eu escrevi. Ele lida com apenas a entrada de Numbers e também maximumSize. É um controle personalizado que pode ser usado em FXML e também as propriedades podem ser definidas no próprio FXMl.

package fxml;

import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextField;

public class CustomTextField extends TextField {

/**
 * numericOnly property if set, will allow accept only numeric input.
 */
private BooleanProperty numericOnly = new SimpleBooleanProperty(this,
        "numericOnly", false);

public final boolean isNumericOnly() {
    return numericOnly.getValue();
}

public final void setNumericOnly(boolean value) {
    numericOnly.setValue(value);
}

public final BooleanProperty numericOnlyProperty() {
    return numericOnly;
}

/**
 * maxSize property , determines the maximum size of the text that can be
 * input.
 */
public IntegerProperty maxSize = new IntegerPropertyBase(1000) {

    @Override
    public String getName() {
        return "maxSize";
    }

    @Override
    public Object getBean() {
        return CustomTextField.this;
    }
};

public final IntegerProperty maxSizeProperty() {
    return maxSize;
};

public final int getMaxSize() {
    return maxSize.getValue();
}

public final void setMaxSize(int value) {
    maxSize.setValue(value);
}

/**
 * this method is called when user inputs text into the textField
 */
@Override
public void replaceText(int start, int end, String text) {
    if (numericOnly.getValue() && !text.equals("")) {
        if (!text.matches("[0-9]")) {
            return;
        }
    }
    if (getText().length() < getMaxSize() || text.equals("")) {
        super.replaceText(start, end, text);
    }
}

/**
 * this method is called when user pastes text into the textField
 */
@Override
public void replaceSelection(String text) {
    if (numericOnly.getValue() && !text.equals("")) {
        if (!text.matches("[0-9]+")) {
            return;
        }
    }
    super.replaceSelection(text);
    if (getText().length() > getMaxSize()) {
        String maxSubString = getText().substring(0, getMaxSize());
        setText(maxSubString);
        positionCaret(getMaxSize());
    }
}

}

2
AJAY PRAKASH

Solução combinada de BaiJiFeiLong e AJAY PRAKASH para suportar entradas decimais

package com.mazeworks.cloudhms.view.components;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextField;

public class NumericTextField extends TextField {

    /**
     * numericOnly property if set, will allow accept only numeric input.
     */
    private BooleanProperty numericOnly = new SimpleBooleanProperty(this,
            "numericOnly", false);

    public final boolean isNumericOnly() {
        return numericOnly.getValue();
    }

    public final void setNumericOnly(boolean value) {
        numericOnly.setValue(value);
    }

    public final BooleanProperty numericOnlyProperty() {
        return numericOnly;
    }

    /**
     * maxSize property, determines the maximum size of the text that 
     can be
     * input.
     */
    public IntegerProperty maxSize = new IntegerPropertyBase(1000) {

        @Override
        public String getName() {
            return "maxSize";
        }

        @Override
        public Object getBean() {
            return NumericTextField.this;
        }
    };

    public final IntegerProperty maxSizeProperty() {
        return maxSize;
    }

    ;

    public final int getMaxSize() {
        return maxSize.getValue();
    }

    public final void setMaxSize(int value) {
        maxSize.setValue(value);
    }

    /**
     * this method is called when user inputs text into the textField
     */
    @Override
    public void replaceText(int start, int end, String text) {
        if (numericOnly.getValue() && !text.equals("")) {
            if (!text.matches("^[0-9]*\\.?[0-9]*$")) {
                return;
            }
        }
        if (getText().length() < getMaxSize() || text.equals("")) {
            super.replaceText(start, end, text);
        }
    }

    /**
     * this method is called when user pastes text into the textField
     */
    @Override
    public void replaceSelection(String text) {
        if (numericOnly.getValue() && !text.equals("")) {
            if (!text.matches("^[0-9]*\\.?[0-9]*$")) {
                return;
            }
        }
        super.replaceSelection(text);
        if (getText().length() > getMaxSize()) {
            String maxSubString = getText().substring(0, getMaxSize());
            setText(maxSubString);
            positionCaret(getMaxSize());
        }
    }
}
0
Mambisi Zempare

Herdar de TextField e substituir replaceText como tal, para obter um Double values ​​only TextField:

@Override
public void replaceText(int start, int end, String text) {
    String preText = getText(0, start);
    String afterText = getText(end, getLength());
    String toBeEnteredText = preText + text + afterText;

    // Treat the case where the user inputs proper text and is not inputting backspaces or other control characters
    // which would be represented by an empty text argument:
    if (!text.isEmpty() && text.matches("\\d|\\.")) {
        Logger.getAnonymousLogger().info("Paring non-empty.");
        try {
            Logger.getAnonymousLogger().info("Parsing " + toBeEnteredText);
            Double.parseDouble(toBeEnteredText);
            super.replaceText(start, end, text);
        } catch (Exception ignored) {
        }
    }

    // If the user used backspace or del, the result text is impossible to not parse as a Double/Integer so just
    // enter that text right ahead:
    if (text.isEmpty()) {
        super.replaceText(start, end, text);
    }
}
0
brokenthorn