javafxevent-handlingchangelistener

Shared ChangeListener VS multiple ChangeListeners?


I have several TextFields that need to perform certain actions when focus is lost, like: trimming text, comparing it to initial one, checking it and if it's valid - saving. Which way would be better to handle it? By creating one ChangeListener with switch statement and sharing it among my Textfields:

ChangeListener<Boolean> focusHandler = new ChangeListener<Boolean>() {

    private String initialValue = null;

    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean focus) {

        ReadOnlyBooleanProperty booleanProperty = (ReadOnlyBooleanProperty) observable;
        TextField textField = (TextField)booleanProperty.getBean();

        if (focus) { // gained focus

            initialValue = textField.getText();

        } else { // lost focus

            trimText(textField);

            if(!textField.getText().equals(initialValue)){  //text was changed
                if(textField.getText().isEmpty()){  //text was erased

                    switch (textField.getId()) {
                    case CoreConstants.AREA_SIZE_TF:
                        emptyAreaSize();
                        break;
                    case CoreConstants.NUMBER_OF_PEOPLE_TF:
                        emptyPeopleLiving();
                        break;
                    default:
                        //no actions
                        break;
                    }

            }else{

                switch (textField.getId()) {
                    case CoreConstants.NAME_TF:
                        System.out.println("Execute name code!");
                        break;
                    case CoreConstants.SURNAME_TF:
                        System.out.println("Execute last name code!");
                        break;
                    case CoreConstants.MAIL_TF:
                        System.out.println("Execute mail code!");
                        break;
                    default:
                        //no actions
                        break;
}}}}}};
nameTextField.focusedProperty().addListener(focusHandler);
surnameTextField.focusedProperty().addListener(focusHandler);

Or by creating separate ChangeListener for each of TextFields like this:

numberOfPeopleTextField.focusedProperty().addListener(new ChangeListener<Boolean>() {

    private String initialValue = null;

    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean focus) {


        if (focus) { // gained focus

            initialValue = numberOfPeopleTextField.getText();

        } else { // lost focus

            trimText(numberOfPeopleTextField);

            if(!numberOfPeopleTextField.getText().equals(initialValue)){ //text was changed

                if(numberOfPeopleTextField.getText().isEmpty()){
                    emptyPeopleLiving();
                }else{
                    mailInfoUpdate("mail");
                }
            }
        }
    }
});

mailTextField.focusedProperty().addListener(new ChangeListener<Boolean>() {

    private String initialValue = null;

    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean focus) {

        if (focus) { // gained focus

            initialValue = mailTextField.getText();

        } else { // lost focus
            trimText(mailTextField);
            if(!mailTextField.getText().equals(initialValue)){  //text was changed
                mailInfoUpdate("mail");
            }
        }
    }
});

Creating several ChangeListeners VS one shared ChangeListener what is better to use and why?


Solution

  • I'd go with option 3: Create a class containing the common code and use different handlers.

    class FocusChangeListener implements ChangeListener<Boolean> {
        private final TextField textField;
        private final Consumer<? super String> changeHandler;
        private String initialValue = null;
    
        FocusChangeListener(TextField textField, Consumer<? super String> changeHandler) {
            this.textField = textField;
            this.changeHandler = changeHandler;
        }
    
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean focus) {
    
            if (focus) { // gained focus
                initialValue = textField.getText();
            } else { // lost focus
                trimText(textField);
                if(changeHandler != null && !textField.getText().equals(initialValue)) { //text was changed
                    changeHandler.accept(textField.getText());
                }
            }
        }
    
    }
    
    numberOfPeopleTextField.focusedProperty().addListener(new FocusChangeListener(numberOfPeopleTextField, text -> {
        if(text.isEmpty()){
            emptyPeopleLiving();
        } else{
            mailInfoUpdate("mail");
        }
    }));
    mailTextField.focusedProperty().addListener(new FocusChangeListener(mailTextField, text -> mailInfoUpdate("mail")));
    

    If it suits your needs better, you could also replace the Consumer with a abstract method in FocusChangeListener...

    Switching on the id is a bad idea since it does not seperate the concerns in addition to using magic strings. Reimplementing the whole listener is not a good idea either since it makes the code harder to maintain...