18

问题

当从 中删除项目时,会触发ObservableList一个更改事件getFrom(),其中给出了删除的位置并getRemoved()给出了已删除项目的列表。文档说:

getRemoved()方法返回已被替换或从列表中删除的元素列表。

它没有这样说明,但我认为它暗示项目列表是原始列表的连续子列表。我已经用这个假设写了很多代码,但是现在遇到了TreeTableView's selection model 的困难,它的行为不是那样的。

例子

以具有三个“节点”行的简单树表为例。如果我选择这三行...

选择了三行

...然后单击并仅选择中间行...

在此处输入图像描述

...触发的更改事件treeTableView.getSelectionModel().getSelectedItems()如下所示:

{ [TreeItem [ value: Node 1 ], TreeItem [ value: Node 3 ]] removed at 0,  }

在单个更改事件中,它报告“节点 1”和“节点 3”已从selectedItems列表的索引 0 中删除。

我本来希望该Change对象有两个单独的删除事件,由next()调用分隔。第一次调用next()会告诉我“节点 1”在索引 0 处被删除,第二次调用next()会告诉我“节点 3”在索引 1 处被删除。但是不,我得到一个事件,两行都在一次。

问题

真的可以getRemoved()退回不连续的物品吗?这是我对列表更改事件如何工作的误解,还是其中的一个错误TreeTableView

通常我会犹豫归咎于标准库,但这不会是我在 JavaFX 中发现的第一个错误,所以这并非不可想象。


更新

如果我添加对 的调用setShowRoot(false),则行为会发生变化。我得到了我的期望,删除分为两部分:

{ [TreeItem [ value: Node 1 ]] removed at 0, [TreeItem [ value: Node 3 ]] removed at 1,  }

另外,这是我的MCVE

import java.util.*;

import javafx.application.*;
import javafx.beans.property.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.stage.*;

public class TreeTableSelectionEvents extends Application {
    public void start(Stage stage) {
        // Root node.
        TreeItem<String> root = new TreeItem<>("Root");

        root.setExpanded(true);

        root.getChildren().setAll(Arrays.asList(
            new TreeItem<>("Node 1"),
            new TreeItem<>("Node 2"),
            new TreeItem<>("Node 3")
        ));

        // Single column.
        TreeTableColumn<String, String> column = new TreeTableColumn<>("Column");

        column.setPrefWidth(150);
        column.setCellValueFactory((TreeTableColumn.CellDataFeatures<String, String> p) -> {
            return new ReadOnlyStringWrapper(p.getValue().getValue());
        });

        // Tree table.
        TreeTableView<String> table = new TreeTableView<>(root);

        table.getColumns().add(column);
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        // table.setShowRoot(false);

        table.getSelectionModel().getSelectedItems().addListener(
            (ListChangeListener.Change<? extends TreeItem<String>> change) -> {
                System.out.printf("item change = %s, list is now %s%n", change, change.getList());
            }
        );

        table.getSelectionModel().getSelectedIndices().addListener(
            (ListChangeListener.Change<? extends Integer> change) -> {
                System.out.printf("index change = %s, list is now %s%n", change, change.getList());
            }
        );

        // Stage.
        stage.setScene(new Scene(table));
        stage.show();
    }
}
4

1 回答 1

2

你是对的,该事件应该包含两个单独的“删除”更改。从 1.8.0_74 开始,TreeTableView 的选择模型似乎被彻底破坏了。它甚至与 TreeView 的选择模型不一致,后者也很棘手(但不那么严重)。有如此多的故障模式、现有的错误和回归错误,很难判断 Oracle 是否意识到了问题。我建议提交另一个错误。下面的代码提供了一个不错的沙箱来玩功能。

public class Test extends Application {
    public void start(Stage pStage) {
        pStage.setTitle("Test");

        final TabPane tabPane = new TabPane();

        tabPane.getTabs().addAll(
            Stream.of(true, false).map(
                pIsTreeTable -> {
                    final Tab result = new Tab(pIsTreeTable ? "TreeTableView" : "TreeView");

                    // create tree model
                    final TreeItem<String> root = new TreeItem<>("Root Node");
                    root.setExpanded(true);
                    final Collection<TreeItem<String>> children = IntStream.rangeClosed(
                        1, 5
                    ).mapToObj(pIdx -> new TreeItem<>("Child Node " + pIdx)).collect(
                        Collectors.toList()
                    );

                    // create TreeView or TreeTableView
                    final Control tree;
                    final MultipleSelectionModel<TreeItem<String>> selectionModel;
                    if (pIsTreeTable) {
                        final TreeTableView<String> treeTableView = new TreeTableView<>(
                            root
                        );
                        final TreeTableColumn<String,String> column = new TreeTableColumn<>(
                            "Column"
                        );
                        column.setCellValueFactory(
                            pTreeItem -> new ReadOnlyStringWrapper(
                                pTreeItem.getValue().getValue()
                            )
                        );
                        treeTableView.getColumns().add(column);

                        tree = treeTableView;
                        selectionModel = treeTableView.getSelectionModel();
                    } else {
                        final TreeView<String> treeView = new TreeView<>(root);

                        tree = treeView;
                        selectionModel = treeView.getSelectionModel();
                    }
                    selectionModel.setSelectionMode(SelectionMode.MULTIPLE);

                    // add buttons
                    final ToggleButton childrenBtn = new ToggleButton("Children");
                    childrenBtn.selectedProperty().addListener(
                        (pObservable, pOldVal, pNewVal) -> {
                            if (pNewVal) {
                                root.getChildren().addAll(children);
                            } else {
                                root.getChildren().clear();
                            }
                        }
                    );
                    childrenBtn.setSelected(true);
                    final ToggleButton showRootBtn = new ToggleButton("Show Root");
                    showRootBtn.setSelected(true);
                    (
                        pIsTreeTable ?
                        ((TreeTableView<?>) tree).showRootProperty() :
                        ((TreeView<?>) tree).showRootProperty()
                    ).bind(showRootBtn.selectedProperty());

                    // 'getSelectedItems()' tab
                    final Tab selectedItemsTab = new Tab("getSelectedItems()");
                    final TextArea selectedItemsTextArea = new TextArea();
                    selectionModel.getSelectedItems().addListener(
                        (ListChangeListener<TreeItem<String>>) pChange -> {
                            while (pChange.next()) {
                                if (pChange.getRemovedSize() > 0) {
                                    selectedItemsTextArea.appendText(
                                        "Removed " + pChange.getRemoved() + '\n'
                                    );
                                }
                                if (pChange.getAddedSize() > 0) {
                                    selectedItemsTextArea.appendText(
                                        "Added " + pChange.getAddedSubList() + '\n'
                                    );
                                }
                            }
                            selectedItemsTextArea.appendText(
                                "Selection: " + pChange.getList() + "\n\n"
                            );
                        }
                    );
                    selectedItemsTab.setContent(selectedItemsTextArea);

                    // 'getSelectedItem()' tab
                    final Tab selectedItemTab = new Tab("getSelectedItem()");
                    final TextArea selectedItemTextArea = new TextArea();
                    selectionModel.selectedItemProperty().addListener(
                        (pObservable, pOldVal, pNewVal) -> {
                            selectedItemTextArea.appendText("Selected " + pNewVal + '\n');
                        }
                    );
                    selectedItemTab.setContent(selectedItemTextArea);


                    // display selection data in text area
                    final TabPane selectionTabPane = new TabPane();
                    selectionTabPane.getTabs().addAll(selectedItemsTab, selectedItemTab);

                    final SplitPane splitPane = new SplitPane(
                        tree, new HBox(showRootBtn, childrenBtn), selectionTabPane
                    );
                    splitPane.setOrientation(Orientation.VERTICAL);

                    result.setContent(splitPane);

                    return result;
                }
            ).collect(Collectors.toList())
        );

        pStage.setScene(new Scene(tabPane, 300, 450));
        pStage.show();
    }

    public static void main(String[] pArgs) {launch(pArgs);}
}

相关问题:

  1. 按下“Ctrl”按钮
  2. 选择“子节点 2”
  3. 选择“子节点 3”
  4. 选择“子节点 1”

ListChangeListener.Change 事件意味着选择了“子节点 2”。

  1. 选择“根节点”
  2. 取消选择“显示根”按钮

“子节点 1”被选中,但没有广播选择事件。

  1. 选择“子节点 2”
  2. 取消选择“儿童”按钮

“选择”列表包含一个空值。

  1. 选择“子节点 1”
  2. 折叠“根节点”

ListChangeListener.Change 事件不包括任何删除。

  1. 取消选择“儿童”按钮
  2. 选择“根节点”
  3. 取消选择“显示根”按钮

既不为 TreeView 也不为 TreeTableView 广播事件。从那里,如果按下“显示根”按钮,TreeView 会广播事件,但 TreeTableView 不会。

于 2016-02-06T19:57:43.970 回答