Search code examples
javauser-interfacejavafxdata-bindingnumber-formatting

Formatting integer while binding to label


I am trying to format an integer while binding it to the text property of a label.

I know I can use setText() in my value setter, but I'd rather do it the proper way through binding.

In my controller initialization I had:

sec = new SimpleIntegerProperty(this,"seconds");
secondsLabel.textProperty().bind(Bindings.convert(sec));

But when the number of seconds dropped below 10, it showed as a single digit, but I want it to stay as two digits. So I tried changing the Binding to the following:

 secondsLabel.textProperty().bind(Bindings.createStringBinding(() -> {
        NumberFormat formatter = NumberFormat.getIntegerInstance();
        formatter.setMinimumIntegerDigits(2);
        if(sec.getValue() == null) {
            return "";
        }else {
            return formatter.format(sec.get());
        }
    }));

This will format it, but when I overwrite it sec.set(newNumber); the value doesn't change.

I also tried this:

secondsLabel.textProperty().bind(Bindings.createStringBinding(() -> {
            if(sec.getValue() == null) {
                return "";
            }else {
                return String.format("%02d", sec.getValue());
            }
        }));

But that did the same thing. Loads up fine, shows two digits, but when the number was changed via sec.set(newNumber); nothing changed. The number will never go higher than sixty or lower than zero


Solution

  • You need to tell your binding that it should be invalidated any time the sec property is invalidated. Bindings.createStringBinding(...) takes a varargs parameter after the function that should be passed any properties to which the binding needs to bind. You can directly adapt your code as follows:

    secondsLabel.textProperty().bind(Bindings.createStringBinding(() -> {
        NumberFormat formatter = NumberFormat.getIntegerInstance();
        formatter.setMinimumIntegerDigits(2);
        if(sec.getValue() == null) {
            return "";
        }else {
            return formatter.format(sec.get());
        }
    }, sec));
    

    or

    secondsLabel.textProperty().bind(Bindings.createStringBinding(() -> {
        if(sec.getValue() == null) {
            return "";
        }else {
            return String.format("%02d", sec.getValue());
        }
    }, sec));
    

    As @fabian points out, IntegerProperty.get() never returns null, so you can remove the null check and just do:

    secondsLabel.textProperty().bind(Bindings.createStringBinding(
        () -> String.format("%02d", sec.getValue()),
        sec));
    

    and there is a convenience version of this in the bindings API:

    secondsLabel.textProperty().bind(Bindings.format("%02d", sec));