如何在JavaFX中使用ListenerHandle?

时间:2018-03-21 17:11:30

标签: javafx changelistener

我看过this article on ListenerHandles,但我无法理解如何在我的代码中实现它。我有ChangeListener我分配给CheckBoxes。我需要更改CheckBoxes的值而不Listeners注意到它。

我创建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");
            }
        };

1 个答案:

答案 0 :(得分:0)

首先,注意几点:

  1. 您的代码无法正常工作,因为每次调用时checkBoxListener()都会创建一个不同的侦听器。因此,您尝试删除的侦听器不是您最初添加的侦听器,因此对removeListener(...)的调用不执行任何操作。更糟糕的是,当您尝试恢复原始内容时,稍后会添加不同的侦听器。因此,您应该发现在调用deselectCheckBoxes()方法后选中复选框会多次调用侦听器。
  2. 对我来说,完全需要这样做通常是设计不佳的标志。你真的不应该关心如何取消选中复选框(即以编程方式或由用户)。控制器应该真正调用模型上的方法,以便模型与UI的状态同步。从这个角度来看,取消选中复选框的原因并不重要。
  3. 请注意,此功能已实现&#34;开箱即用&#34;作者:Tomas Mikula的ReactFX framework。 (基本上,您可以订阅&#34;事件流&#34;,其中可以包含属性更改流。您可以制作这些流&#34;可暂停&#34;并在特定操作期间暂停它们。)
  4. 无论如何,假设你出于某些原因确实需要这个,正如我所理解的那样,意图是你会做这样的事情:

    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();
    

    和&#34;关闭&#34;处理,你会做

    // ignore this change:
    handle.detach();
    checkBox.setSelected(false);
    handle.attach();
    

    请注意,要使其工作,您仍然需要保留对句柄的引用(您不再需要保持对属性或节点的单独引用,并保持它们&#34;已连接&#34;) 。在您的情况下,由于您有多个复选框,因为您有多个复选框和多个侦听器,您需要以下内容:

    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或类似的方法做到这一点,但是那时你几乎没有什么收获,而只是简单地保留对听众的引用并删除它们。