I read this article on ListenerHandles, but I couldn't understand how to implement it in my code. I have a ChangeListener
which I assign to my CheckBoxes
. I need to change value of CheckBoxes
without Listeners
noticing it.
My code for creating CheckBoxes
:
for (int i = 0; i<2; i++) {
CheckBox checkBox = new CheckBox();
checkBox.setText("CheckBox "+(i+1));
checkBox.setAlignment(Pos.TOP_LEFT);
checkBox.selectedProperty().addListener(checkBoxListener(checkBox));
myVBox.getChildren().add(checkBox);
}
ChangeListener
code:
private ChangeListener<Boolean> checkBoxListener(CheckBox checkBox) {
return new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean selected) {
if (selected) {
System.out.println("CheckBox got selected");
} else {
System.out.println("CheckBox got deselected");
}
}
};
}
Code that needs to get done without Listener
noticing:
public void deselectCheckBoxes(){
for(Node node : myVBox.getChildren()){
CheckBox checkBox = (CheckBox) node;
System.out.println("Removing listener");
checkBox.selectedProperty().removeListener(checkBoxListener(checkBox));
checkBox.setSelected(false);
System.out.println("Adding listener back");
checkBox.selectedProperty().addListener(checkBoxListener(checkBox));
}
}
Listener
still noticing that I'm deselecting CheckBox
through this function. Can someone explain how I could implement ListenerHandle
way in my code?
Thank you very much for your answer. I adjusted ChangeListener
, so that I can get object from my CheckBox
:
ChangeListener<Boolean> listener = (obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
BooleanProperty booleanProperty = (BooleanProperty) obs;
CheckBox checkBox = (CheckBox)booleanProperty.getBean();
System.out.println("Selected: "+(Bank)checkBox.getUserData());
} else {
System.out.println("Not selected");
}
};
First, a few notes:
checkBoxListener()
creates a different listener every time you invoke it. So the listener you attempt to remove is not the listener you initially added, and so the call to removeListener(...)
does nothing. Worse, you add a different listener later when you try to restore the original. Consequently, you should find that checking the check box after the deselectCheckBoxes()
method has been called invokes the listener multiple times.Anyway, assuming you really need this for some reason, as I understand it, the intention is that you would do something like this:
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
public class ListenerHandle<T> {
private final ObservableValue<T> observable ;
private final ChangeListener<? super T> listener ;
private boolean attached ;
public ListenerHandle(ObservableValue<T> observable, ChangeListener<? super T> listener) {
this.observable = observable ;
this.listener = listener ;
}
public void attach() {
if (! attached) {
observable.addListener(listener);
attached = true ;
}
}
public void detach() {
if (attached) {
observable.removeListener(listener);
attached = false ;
}
}
}
Then you can use this as
ChangeListener<Boolean> listener = (obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
System.out.println("Selected");
}
else {
System.out.println("Not selected");
}
};
ListenerHandle<Boolean> handle = new ListenerHandle<>(checkBox.selectedProperty(), listener);
handle.attach();
and to "turn off" handling, you would do
// ignore this change:
handle.detach();
checkBox.setSelected(false);
handle.attach();
Note that to make this work, you still need to keep a reference to the handle (you just no longer necessarily need to keep a separate reference to the property or node, and keep them "connected"). In your case, since you have multiple check boxes, since you have multiple check boxes and multiple listeners, you would need something like:
private List<ListenerHandle<Boolean>> checkBoxListenerHandles = new ArraryList<>();
// ...
ChangeListener<Boolean> listener = (obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
System.out.println("CheckBox got selected");
} else {
System.out.println("CheckBox got deselected");
}
}
for (int i = 0; i<2; i++) {
CheckBox checkBox = new CheckBox();
checkBox.setText("CheckBox "+(i+1));
checkBox.setAlignment(Pos.TOP_LEFT);
ListenerHandle<Boolean> handle = new ListenerHandle<>(checkBox.selectedProperty(), listener);
handle.attach();
checkBoxListenerHandles.add(handle);
myVBox.getChildren().add(checkBox);
}
and then
public void deselectCheckBoxes(){
checkBoxListenerHandles.forEach(ListenerHandle::detach);
for(Node node : myVBox.getChildren()){
CheckBox checkBox = (CheckBox) node;
checkBox.setSelected(false);
}
checkBoxListenerHandles.forEach(ListenerHandle::attach);
}
Note that if you wanted to deselect (and ignore) a specific check box, you would have to know which handle belonged to that particular check box. You could do that with a Map
, or similar, but at that point you have pretty much gained nothing compared to simply keeping references to the listeners and removing them.