【问题标题】:How to use ListenerHandle in JavaFX?如何在 JavaFX 中使用 ListenerHandle?
【发布时间】:2018-03-21 17:11:29
【问题描述】:

我阅读了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");
            }
        };

【问题讨论】:

    标签: javafx changelistener


    【解决方案1】:

    首先,有几点说明:

    1. 您的代码不起作用,因为您的checkBoxListener() 每次调用它时都会创建一个不同的侦听器。因此,您尝试删除的侦听器不是您最初添加的侦听器,因此对 removeListener(...) 的调用什么也不做。更糟糕的是,您稍后在尝试恢复原始时添加了一个 不同 侦听器。因此,您应该会发现在调用 deselectCheckBoxes() 方法后选中该复选框会多次调用侦听器。
    2. 对我来说,完全需要这样做通常是糟糕设计的标志。您真的不应该关心如何取消选中复选框(即以编程方式或由用户)。控制器应该真正调用模型上的方法,以便模型与 UI 的状态同步。从这个角度来看,复选框被取消选中的原因应该无关紧要。
    3. 请注意,此功能是由 Tomas Mikula 的ReactFX framework“开箱即用”实现的。 (基本上,您可以订阅“事件流”,其中可以包括属性更改流。您可以使这些流“暂停”并在特定操作期间暂停它们。)

    无论如何,假设您出于某种原因确实需要它,据我了解,目的是您会做这样的事情:

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

    要“关闭”处理,你会这样做

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

    请注意,要完成这项工作,您仍然需要保留对句柄的引用(您不再需要保留对属性或节点的单独引用,并保持它们“连接”)。在您的情况下,由于您有多个复选框,因为您有多个复选框和多个侦听器,您需要类似:

    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 或类似的方法来做到这一点,但是与简单地保留对侦听器的引用并删除它们相比,您几乎没有任何收获。

    【讨论】:

    • 感谢您的回答。我目前正在学习哪种方式最适合处理听众。我需要将CheckBox 对象传递给侦听器,基本上我需要访问连接到CheckBox 内部侦听器的对象,例如checkBox.getUserData()。我可以做这样的事情吗?对不起,如果这听起来有点过于复杂。
    猜你喜欢
    • 1970-01-01
    • 2013-06-01
    • 2021-07-03
    • 2016-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-15
    相关资源
    最近更新 更多