为什么与CheckBox关联的ChangeListener在TreeCell中多次运行

时间:2013-12-17 20:44:45

标签: java javafx

我遇到了一个我无法弄清楚的问题。我正在使用名为treeModel的TreeView并使用setCellFactory设置单元格,如代码所示。现在在updateItem中,我将CheckBox设置为图形,并将其与名为CheckBoxTreeItemModel的CheckBoxTreeItem自定义类相关联。现在,每次updateItem运行时,都会创建一个新的CheckBox,并为其创建新的ChangeListener

现在起初一切看起来都很正常。然后我展开根的直接子节点,并开始检查项目,但是监听器似乎被多次调用。对于展开的每个级别的TreeItem,这是在root的一个后代上调用监听器的次数。如果我点击一个孩子,一些人离开父母,那么这些听众也会被多次调用。它的奇怪行为可能很难解释,但重点是我不认为听众被多次调用。它好像是缓存的。问题代码如下。任何帮助理解为什么会发生这种情况将不胜感激。

    treeModel.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {

        @Override
        public TreeCell<String> call(TreeView<String> param) {
            return new TreeCell<String>() {
                @Override
                public void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    final TreeCell<String> currCell = this;
                    this.setOnMouseClicked(new EventHandler<MouseEvent>() {
                        /*mouse event stuff completely unrelated to problem*/
                    });
                    if (empty) {
                        setText(null);
                        setGraphic(null);
                    } 
                    else {

                        TreeItem<String> treeItem = getTreeItem();
                        if (treeItem instanceof CheckBoxTreeItemModel) {
                            System.out.println("Being called.");
                            final CheckBoxTreeItemModel chkTreeItem = (CheckBoxTreeItemModel) treeItem;

                            setText(item.toString());
                            CheckBox chk = new CheckBox();
                            chk.setSelected(chkTreeItem.getDeleteTick());

                            if(chkTreeItem.getListener() == null) {
                                ChangeListener<Boolean> listener = new ChangeListener<Boolean>() {
                                    @Override
                                    public void changed(ObservableValue<? extends Boolean> observable,
                                            Boolean oldValue, Boolean newValue) {
                                        if(newValue) {
                                            //was checked
                                            System.out.println(chkTreeItem.toString()+" was checked!");
                                            chkTreeItem.setDeleteTick(newValue);    
                                        }
                                        else {
                                            System.out.println(chkTreeItem.toString()+" was un-checked!");
                                            chkTreeItem.setDeleteTick(newValue);    
                                        }

                                    }//end of changed method
                                };
                                chkTreeItem.setListener(listener);
                            }
                            chk.selectedProperty().removeListener(chkTreeItem.getListener());
                            chk.selectedProperty().addListener(chkTreeItem.getListener());

                            chk.indeterminateProperty().bindBidirectional(chkTreeItem.indeterminateProperty());
                            chk.selectedProperty().bindBidirectional(chkTreeItem.selectedProperty());
                            setGraphic(chk);
                        } 
                        else {
                            setText(item.toString());
                            setGraphic(null);
                        }
                    }
                }//end of updateItem
            };
        }//end of the call method
    });

1 个答案:

答案 0 :(得分:0)

建议的方法

我建议您删除大部分代码并使用内置的CheckBoxTreeCellCheckBoxTreeItem类。我不确定内置单元格是否符合您的要求,但即使它们没有,您也可以检查它们的源代码并与您的源代码进行比较,它(应该)开始让您对自己的位置有所了解出错了。

您的代码中的潜在问题

重现您的问题需要的代码数量超过您当前提供的代码数量。但有些事情要寻找:

  1. 删除和添加相同的侦听器毫无意义:

    // first line is redundant. 
    // all listener code is probably unnecessary as you already bindBidirectional.
    chk.selectedProperty().removeListener(chkTreeItem.getListener());
    chk.selectedProperty().addListener(chkTreeItem.getListener());
    
  2. 对于给定的单元格,可能会多次调用
  3. updateItem。不要每次都为该单元创建新节点,而是重用为该单元创建的现有节点。

    // replace with a lazily instantiated CheckBox member reference in TreeCell instance.
    CheckBox chk = new CheckBox();  
    
  4. bindBiDirectional但从未解除绑定值。

    // should also unbind this values.
    chk.indeterminateProperty().bindBidirectional(chkTreeItem.indeterminateProperty());
    chk.selectedProperty().bindBidirectional(chkTreeItem.selectedProperty());
    
  5. 示例代码

    来自内置updateItem代码的示例CheckBoxTreeCell代码:

    public class CheckBoxTreeCell<T> extends DefaultTreeCell<T> {
    . . .
    private final CheckBox checkBox;    
    private ObservableValue<Boolean> booleanProperty;    
    private BooleanProperty indeterminateProperty;
    . . .
    @Override public void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
    
        if (empty) {
            setText(null);
            setGraphic(null);
        } else {
            StringConverter c = getConverter();
    
            TreeItem<T> treeItem = getTreeItem();
    
            // update the node content
            setText(c != null ? c.toString(treeItem) : (treeItem == null ? "" : treeItem.toString()));
            checkBox.setGraphic(treeItem == null ? null : treeItem.getGraphic());
            setGraphic(checkBox);
    
            // uninstall bindings
            if (booleanProperty != null) {
                checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty);
            }
            if (indeterminateProperty != null) {
                checkBox.indeterminateProperty().unbindBidirectional(indeterminateProperty);
            }
    
            // install new bindings.
            // We special case things when the TreeItem is a CheckBoxTreeItem
            if (treeItem instanceof CheckBoxTreeItem) {
                CheckBoxTreeItem<T> cbti = (CheckBoxTreeItem<T>) treeItem;
                booleanProperty = cbti.selectedProperty();
                checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);
    
                indeterminateProperty = cbti.indeterminateProperty();
                checkBox.indeterminateProperty().bindBidirectional(indeterminateProperty);
            } else {
                Callback<TreeItem<T>, ObservableValue<Boolean>> callback = getSelectedStateCallback();
                if (callback == null) {
                    throw new NullPointerException(
                            "The CheckBoxTreeCell selectedStateCallbackProperty can not be null");
                }
    
                booleanProperty = callback.call(treeItem);
                if (booleanProperty != null) {
                    checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);
                }
            }
        }
    }
    . . .
    }