如何在 JavaFX 中使用 ListenerHandle?
How to use ListenerHandle in JavaFX?
我读了 this article on ListenerHandles,但我不明白如何在我的代码中实现它。我有一个 ChangeListener
分配给我的 CheckBoxes
。我需要在 Listeners
没有注意到的情况下更改 CheckBoxes
的值。
我的创建代码 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
代码:
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");
}
}
};
}
需要在 Listener
不注意的情况下完成的代码:
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
仍然注意到我正在通过此功能取消选择 CheckBox
。有人可以解释我如何在我的代码中实现 ListenerHandle
方式吗?
非常感谢您的回答。我调整了 ChangeListener
,这样我就可以从我的 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");
}
};
首先,几点说明:
- 您的代码不起作用,因为您的
checkBoxListener()
每次调用它时都会创建不同的侦听器。因此,您尝试删除的侦听器不是您最初添加的侦听器,因此对 removeListener(...)
的调用没有任何作用。更糟糕的是,当您稍后尝试恢复原始时,您会添加一个 different 侦听器。因此,您会发现在调用 deselectCheckBoxes()
方法后选中复选框会多次调用侦听器。
- 对我来说,需要这样做通常是设计不佳的标志。你真的不应该关心如何复选框被取消(即以编程方式或由用户)。控制器实际上应该在模型上调用方法,以便模型与 UI 的状态同步。从这个角度来看,取消选中复选框的原因应该无关紧要。
- 请注意,此功能由 Tomas Mikula 的 ReactFX framework "out of the box" 实现。 (基本上你可以订阅 "event streams",它可以包括对属性的更改流。你可以使这些流 "suspendible" 并在特定操作期间暂停它们。)
无论如何,假设您出于某种原因确实需要它,据我了解,您的意图是您会做这样的事情:
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 ;
}
}
}
那你就可以用这个了
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();
和"turn off"处理,你会做
// ignore this change:
handle.detach();
checkBox.setSelected(false);
handle.attach();
请注意,要使其正常工作,您仍然需要保留对句柄的引用(您不再需要单独保留对 属性 或节点的引用,而是将它们保留 "connected").在你的情况下,因为你有多个复选框,因为你有多个复选框和多个侦听器,你需要这样的东西:
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);
}
然后
public void deselectCheckBoxes(){
checkBoxListenerHandles.forEach(ListenerHandle::detach);
for(Node node : myVBox.getChildren()){
CheckBox checkBox = (CheckBox) node;
checkBox.setSelected(false);
}
checkBoxListenerHandles.forEach(ListenerHandle::attach);
}
请注意,如果您想取消选择(并忽略)特定复选框,则必须知道哪个句柄属于该特定复选框。您可以使用 Map
或类似的方法来做到这一点,但与简单地保留对侦听器的引用并删除它们相比,此时您几乎没有任何收获。
我读了 this article on ListenerHandles,但我不明白如何在我的代码中实现它。我有一个 ChangeListener
分配给我的 CheckBoxes
。我需要在 Listeners
没有注意到的情况下更改 CheckBoxes
的值。
我的创建代码 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
代码:
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");
}
}
};
}
需要在 Listener
不注意的情况下完成的代码:
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
仍然注意到我正在通过此功能取消选择 CheckBox
。有人可以解释我如何在我的代码中实现 ListenerHandle
方式吗?
非常感谢您的回答。我调整了 ChangeListener
,这样我就可以从我的 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");
}
};
首先,几点说明:
- 您的代码不起作用,因为您的
checkBoxListener()
每次调用它时都会创建不同的侦听器。因此,您尝试删除的侦听器不是您最初添加的侦听器,因此对removeListener(...)
的调用没有任何作用。更糟糕的是,当您稍后尝试恢复原始时,您会添加一个 different 侦听器。因此,您会发现在调用deselectCheckBoxes()
方法后选中复选框会多次调用侦听器。 - 对我来说,需要这样做通常是设计不佳的标志。你真的不应该关心如何复选框被取消(即以编程方式或由用户)。控制器实际上应该在模型上调用方法,以便模型与 UI 的状态同步。从这个角度来看,取消选中复选框的原因应该无关紧要。
- 请注意,此功能由 Tomas Mikula 的 ReactFX framework "out of the box" 实现。 (基本上你可以订阅 "event streams",它可以包括对属性的更改流。你可以使这些流 "suspendible" 并在特定操作期间暂停它们。)
无论如何,假设您出于某种原因确实需要它,据我了解,您的意图是您会做这样的事情:
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 ;
}
}
}
那你就可以用这个了
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();
和"turn off"处理,你会做
// ignore this change:
handle.detach();
checkBox.setSelected(false);
handle.attach();
请注意,要使其正常工作,您仍然需要保留对句柄的引用(您不再需要单独保留对 属性 或节点的引用,而是将它们保留 "connected").在你的情况下,因为你有多个复选框,因为你有多个复选框和多个侦听器,你需要这样的东西:
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);
}
然后
public void deselectCheckBoxes(){
checkBoxListenerHandles.forEach(ListenerHandle::detach);
for(Node node : myVBox.getChildren()){
CheckBox checkBox = (CheckBox) node;
checkBox.setSelected(false);
}
checkBoxListenerHandles.forEach(ListenerHandle::attach);
}
请注意,如果您想取消选择(并忽略)特定复选框,则必须知道哪个句柄属于该特定复选框。您可以使用 Map
或类似的方法来做到这一点,但与简单地保留对侦听器的引用并删除它们相比,此时您几乎没有任何收获。