How to format a text field javafx

Cornelius picture Cornelius · Feb 10, 2016 · Viewed 14k times · Source

I would like to be able to enter the time in a javafx text field in the format hh:mm:ss. How would I go about formatting the text field? I already got the regex method so it only accepts numbers as inputs:

public void format(TextField t, String regex){
    TextFormatter<String> formatter = new TextFormatter<String>( change -> {
        change.setText(change.getText().replaceAll(regex, ""));
        return change; 


    });
    t.setTextFormatter(formatter);
}

and I also got a method that limits the amount of characters allowed in the textfield:

public void limitLength(int maxLength, TextField t){
    t.lengthProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> observable,
                Number oldValue, Number newValue) {
            if (newValue.intValue() > oldValue.intValue()) {
                // Check if the new character is greater than LIMIT
                if (t.getText().length() >= maxLength) {

                    t.setText(t.getText().substring(0, maxLength));
                }
            }
        }
    });
}

However none of this helps me get the time format I want. The ideal would be the initial text being 00:00:00 and the user ONLY being able to edit the digits, unable to touch the colons. It would also be awesome if I could be able to make sure that between each colon there are 2 digits, ie the user should be unable to move around the digits, like this 0:000:00.

Thanks for any help.

Answer

fabian picture fabian · Feb 10, 2016

Using SimpleDateFormat

There is already a class that is designed for formatting dates that you can use with the TextField's textFormatter: SimpleDateFormat

TextField tf = new TextField();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
tf.setTextFormatter(new TextFormatter<>(new DateTimeStringConverter(format), format.parse("00:00:00")));

See javadoc for SimpleDateFormat for a more detailed decription of the meaning of the characters.

Using 3 different TextFields

You could also use 3 different TextFields and remove background and border and put them in a HBox to get non-editable :s:

TextField hours = new TextField();
TextField minutes = new TextField();
TextField seconds = new TextField();
StringConverter<Integer> minSecConverter = new IntRangeStringConverter(0, 59);
minutes.setTextFormatter(new TextFormatter<>(minSecConverter, 0));
seconds.setTextFormatter(new TextFormatter<>(minSecConverter, 0));
hours.setTextFormatter(new TextFormatter<>(new IntRangeStringConverter(0, 23), 0));
prepareTextField(hours);
prepareTextField(minutes);
prepareTextField(seconds);
    
HBox fields = new HBox(hours, createLabel(),minutes, createLabel(), seconds);
fields.setPadding(new Insets(4));
fields.setStyle("-fx-background-color: white;");
public static void prepareTextField(TextField tf) {
    tf.setAlignment(Pos.CENTER);
    tf.setBackground(Background.EMPTY);
    tf.setBorder(Border.EMPTY);
    tf.setPadding(Insets.EMPTY);
    tf.setPrefColumnCount(2);
}

public static class IntRangeStringConverter extends StringConverter<Integer> {

    private final int min;
    private final int max;

    public IntRangeStringConverter(int min, int max) {
        this.min = min;
        this.max = max;
    }
    
    @Override
    public String toString(Integer object) {
        return String.format("%02d", object);
    }

    @Override
    public Integer fromString(String string) {
        int integer = Integer.parseInt(string);
        if (integer > max || integer < min) {
            throw new IllegalArgumentException();
        }

        return integer;
    }

}

public static Label createLabel() {
    Label label = new Label(":");
    label.setPrefWidth(3);
    return label;
}