Damienknight
Damienknight

Reputation: 1896

Control keyboard input into javafx TextField

I want to control the input into a Javafx TextField so that I can allow only Numeric input, and so that if the max characters are exceeded, then no change will be made to the textbox.

edit: Based on a recommendation in the comments, I used the method suggested by the JavaFX project lead. It works great to stop letters from being entered. I just need it to also filter special characters. I tried changing the filter to (text.matchs("[0-9]") but that did not allow backspace to be entered.

edit2: Figured out a filter on special chars and length. Here is my final code. Thanks for the input fellas.

Here is the TextField class i have created:

import javafx.scene.control.TextField;

public class AttributeTextField extends TextField{

    public AttributeTextField() {
        setMinWidth(25);
        setMaxWidth(25);
    }

    public void replaceText(int start, int end, String text) {
        String oldValue = getText();
        if (!text.matches("[a-z]") && !text.matches("[\\\\!\"#$%&()*+,./:;<=>?@\\[\\]^_{|}~]+")) {
            super.replaceText(start, end, text);
        }
        if (getText().length() > 2 ) {
            setText(oldValue);
        }
    }

    public void replaceSelection(String text) {
        String oldValue = getText();
        if (!text.matches("[a-z]") && !text.matches("[\\\\!\"#$%&()*+,./:;<=>?@\\[\\]^_{|}~]+")) {
            super.replaceSelection(text);
        }
        if (getText().length() > 2 ) {
            setText(oldValue);
        }
    }
}

Note: I have read What is the recommended way to make a numeric TextField in JavaFX? this post, and this solution does not work for me. It only gets fired after the number has been entered. Meaning someone could type alphabetic text into the box, and it would allow it until they moved focus away from the textfield. Also, they can enter numbers larger than allowed, but validation happens not on each keypress, but instead after focus shift ('changed' event).

Upvotes: 9

Views: 35260

Answers (6)

user7164105
user7164105

Reputation:

Try this solution add this function in your controller , you must add it on the keyPressed Action of your text field.

@FXML
void verifnum(KeyEvent event) {

    txt.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observable, String oldValue,
                String newValue) {
            if (!newValue.matches("\\d*")) {
                txt.setText(newValue.replaceAll("[^\\d]", ""));
            }
        }
    });
}

Upvotes: 0

Matteo
Matteo

Reputation: 327

I have created a customized Textfield that can be added to Java Builder FX (using 'import JAR/FXML file...').

With this TextField can be set

  1. the characters or numbers allowed
  2. if there is or not the space character
  3. if the input is only CAPITAL (the shown output is in capital)
  4. and the lenght.

Of course can be improoved, but it's quite usefull. Hope that this will help someone :)

FX Project LimitedTextField With this project can be created the 'LimitedTextField.jar' file to import in your application or in java builder FX.

CustomControlExample.java

package limitedtextfield;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class CustomControlExample extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        LimitedTextField customControl = new LimitedTextField();
        customControl.setText("Hello!");

        stage.setScene(new Scene(customControl));
        stage.setTitle("Custom Control");
        stage.setWidth(300);
        stage.setHeight(200);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

custom_control.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<HBox>
    <limitedtextfield.LimitedTextField text="Hello World!"/>
</HBox>

LimitedTextField.java

package limitedtextfield;
import javafx.scene.control.TextField;

public class LimitedTextField extends TextField
{
    private String characters;
    private int max;
    private boolean capital = false;
    private boolean space = true;

    static public final String CharactersNumbers = "[qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890èéòàùì ]";
    static public final String Characters = "[qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNMèéòàùì ]";
    static public final String Numbers = "[1234567890 ]";
    static public final String NumbersPoint = "[1234567890. ]";

    public LimitedTextField(String l){
        super();
        characters = l;
        max=0;
    }

    public LimitedTextField(){
        super();
        characters = "";
        max=0;
    }

    public LimitedTextField(String l, int max){
        super();
        characters = l;
        this.max=max;
        //System.out.println("Costruttore");
    }

    public LimitedTextField(int max){
        super();
        characters = "";
        this.max=max;
    }

    @Override
    public void replaceText(int start, int end, String text)
    {
        if(!characters.equals("")){
            if (validateCh(text))
            {
                text = check(text);
                super.replaceText(start, end, text);
                if(max>0)
                    verifyLengh();
            }
        }else{
            text = check(text);
            super.replaceText(start, end, text);
            if(max>0)
                verifyLengh();
        }
    }

    @Override
    public void replaceSelection(String text)
    {
        if(!characters.equals("")){
            if (validateCh(text))
            {
                text = check(text);
                super.replaceSelection(text);
                if(max>0)
                    verifyLengh();
            }  
        }else{
            text = check(text);
            super.replaceSelection(text);
            if(max>0)
                verifyLengh();
        }
    }

    private boolean validateCh(String text)
    {
        /*
        [abc] Find any of the characters between the brackets 
        [0-9] Find any of the digits between the brackets 
        (x|y) Find any of the alternatives separated with | 
        */
        return ("".equals(text) || text.matches(characters));
    }

    private void verifyLengh() {
        if (getText().length() > max) {
            setText(getText().substring(0, max));//use this line if you want to delete the newer characters inserted
            //setText(getText().substring(getText().length()-max, getText().length()));//use this line if you want to delete the older characters inserted
            positionCaret(max);//set the cursor position
        }

    }

    private String check(String text){
        if(capital)
            text = text.toUpperCase();
        if(!space)
            text = text.replace(" ", "");

        return text;
    }
    public void setLimitCharachters(String s){
        this.characters = s;
    }
    public String getLimitCharachters(){
        return characters;
    }
    public void setMaxLenght(int s){
        this.max= s;
    }
    public int getMaxLenght(){
        return max;
    }
    public boolean getCapital(){
        return this.capital;
    }
    public void setCapital(boolean t){
        this.capital = t;
    }
    public boolean getSpace(){
        return this.space;
    }
    public void setSpace(boolean t){
        this.space = t;
    }
}

Example of use:

MyFxmlApplication.fxml

...
<?import limitedtextfield.*?>
...
<HBox alignment="CENTER_LEFT" spacing="5.0">
      <children>
       <Label text="Name:" />
       <LimitedTextField fx:id="A_Name_S" />
      </children>
     <FlowPane.margin>
     <Insets right="5.0" />
     </FlowPane.margin>
</HBox>
...

MyFxmlApplicationController.fxml

...
import limitedtextfield.LimitedTextField;
@FXML
private LimitedTextField A_Name_S;

...
 @Override
public void initialize(URL url, ResourceBundle rb) {
    A_Name_S.setSpace(false);
    A_Name_S.setCapital(true); 
    A_Name_S.setMaxLenght(20);
    A_Name_S.setLimitCharachters(LimitedTextField.Characters);
}

bye

Upvotes: 2

Michael Sims
Michael Sims

Reputation: 2523

I simply set the 'On Key Typed' event to run this small procedure:

    @FXML public void processKeyEvent(KeyEvent ev) {
    String c = ev.getCharacter();
    if("1234567890".contains(c)) {}
    else {
        ev.consume();
    }
}

It works like a champ!

Upvotes: 2

Damienknight
Damienknight

Reputation: 1896

Final solution. Disallows alphabetic and special characters and enforces character limit.

import javafx.scene.control.TextField;

public class AttributeTextField extends TextField{

    public AttributeTextField() {
        setMinWidth(25);
        setMaxWidth(25);
    }

    public void replaceText(int start, int end, String text) {
        String oldValue = getText();
        if (!text.matches("[A-Za-z]") && !text.matches("[\\\\!\"#$%&()*+,./:;<=>?@\\[\\]^_{|}~]+")) {
            super.replaceText(start, end, text);
        }
        if (getText().length() > 2 ) {
            setText(oldValue);
        }
    }

    public void replaceSelection(String text) {
        String oldValue = getText();
        if (!text.matches("[A-Za-z]") && !text.matches("[\\\\!\"#$%&()*+,./:;<=>?@\\[\\]^_{|}~]+")) {
            super.replaceSelection(text);
        }
        if (getText().length() > 2 ) {
            setText(oldValue);
        }
    }
}

Upvotes: 3

MagicJ
MagicJ

Reputation: 133

the best way is :

    @FXML
private TextField txt_Numeric;
@FXML
private TextField txt_Letters;

@Override
public void initialize(URL url, ResourceBundle rb) {
    /* add Event Filter to your TextFields **************************************************/
    txt_Numeric.addEventFilter(KeyEvent.KEY_TYPED , numeric_Validation(10));
    txt_Letters.addEventFilter(KeyEvent.KEY_TYPED , letter_Validation(10));
}

/* Numeric Validation Limit the  characters to maxLengh AND to ONLY DigitS *************************************/
public EventHandler<KeyEvent> numeric_Validation(final Integer max_Lengh) {
    return new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent e) {
            TextField txt_TextField = (TextField) e.getSource();                
            if (txt_TextField.getText().length() >= max_Lengh) {                    
                e.consume();
            }
            if(e.getCharacter().matches("[0-9.]")){ 
                if(txt_TextField.getText().contains(".") && e.getCharacter().matches("[.]")){
                    e.consume();
                }else if(txt_TextField.getText().length() == 0 && e.getCharacter().matches("[.]")){
                    e.consume(); 
                }
            }else{
                e.consume();
            }
        }
    };
}    
/*****************************************************************************************/

 /* Letters Validation Limit the  characters to maxLengh AND to ONLY Letters *************************************/
public EventHandler<KeyEvent> letter_Validation(final Integer max_Lengh) {
    return new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent e) {
            TextField txt_TextField = (TextField) e.getSource();                
            if (txt_TextField.getText().length() >= max_Lengh) {                    
                e.consume();
            }
            if(e.getCharacter().matches("[A-Za-z]")){ 
            }else{
                e.consume();
            }
        }
    };
}    
/*****************************************************************************************/

Best of luck.

Upvotes: 8

Magcus
Magcus

Reputation: 866

Here is my aproach, two event filters, could be one, in my case i used them in diferent situations, thats why there are two.

Here is the maxValueFilter (in spanglish xD), this one is a class:

public class FilterMaxValue implements EventHandler<KeyEvent> {

        private int maxVal;

        public FilterMaxValue (int i) {
            this.maxVal= i;
        }

        public void handle(KeyEvent arg0) {

            TextField tx = (TextField) arg0.getSource();
            String chara = arg0.getCharacter();
            if (tx.getText().equals(""))
                return;

            Double valor;
            if (chara.equals(".")) {
                valor = Double.parseDouble(tx.getText() + chara + "0");
            } else {
                try {
                    valor = Double.parseDouble(tx.getText() + chara);
                } catch (NumberFormatException e) {
                    //The other filter will prevent this from hapening
                    return;
                }
            }
            if (valor > maxVal) {
                arg0.consume();
            }

        }
    }

And the other event filter (filters the chars), this one is a method:

public static EventHandler<KeyEvent> numFilter() {

        EventHandler<KeyEvent> aux = new EventHandler<KeyEvent>() {
            public void handle(KeyEvent keyEvent) {
                if (!"0123456789".contains(keyEvent.getCharacter())) {
                    keyEvent.consume();

                }
            }
        };
        return aux;
    }

the use in your case would be:

field.addEventFilter(KeyEvent.KEY_TYPED,
                numFilter());
field.addEventFilter(KeyEvent.KEY_TYPED, new FiltroValorMaximo(
                99));

Upvotes: 4

Related Questions