JavaFX TreeTable select children when parent is selected and remove selection from parent

让人想犯罪 __ 提交于 2019-12-11 12:23:11

问题


I have a TreeTable which has maximum depth of 2, e.g.

  • fooType

    -foo

    -foo

If I select fooType I want the program to select automatically all child-items AND deselect the parent item. But when I try this, I always get an IndexOutOfBoundsException.

myTreeTable.getSelectionModel().selectedItemProperty().addListener((obs, ov, nv) -> {
  if (nv.getValue() instanceof fooType) {
    myTreeTable.getSelectionModel().clearSelection(myTreeTable.getSelectionModel().getSelectedIndex());
    if (!nv.isExpanded()) {
      nv.setExpanded(true);
    }
    ObservableList<TreeItem<IfooTreeItem>> children = nv.getChildren();
    for (TreeItem<IfooTreeItem> item : children) {
      annotationsTreeTable.getSelectionModel().select(item);
    }
  }
});

Multi selection mode is enabled.

Any help appreciated.


回答1:


In JavaFX, you are not allowed to change an ObservableList while an existing change to that list is being processed. (Whether or not this is a sensible rule is open to debate, nevertheless, it is a rule.)

The selection model keeps ObservableLists of both the selected indices and the selected items. Part of the processing of changes in those lists is to call listeners on the selected item and selected index. Consequently, you can't change the selection from a listener on the selection itself.

The "proper" way to do this would be to provide your own implementation of the selection model. This is a bit of a pain, as there are a lot of methods to implement, and their use is not well documented. Here is an example, though this is intended as a starting point and is not intended to be production quality:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.MultipleSelectionModel;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class TreeSelectionExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TreeView<String> tree = new TreeView<>();
        TreeItem<String> root = new TreeItem<>();
        tree.setRoot(root);
        tree.setShowRoot(false);

        root.getChildren().add(new TreeItem<>("Item 1"));
        root.getChildren().add(new TreeItem<>("Item 2"));

        root.getChildren().forEach(item -> 
            Stream.of("A", "B").map(s -> new TreeItem<String>(item.getValue()+s))
            .forEach(item.getChildren()::add));

        MultipleSelectionModel<TreeItem<String>> defaultSelectionModel = tree.getSelectionModel() ;
        defaultSelectionModel.setSelectionMode(SelectionMode.MULTIPLE);

        tree.setSelectionModel(new MultipleSelectionModel<TreeItem<String>>() {

            {
                setSelectionMode(SelectionMode.MULTIPLE);
            }

            @Override
            public ObservableList<Integer> getSelectedIndices() {
                return defaultSelectionModel.getSelectedIndices();
            }

            @Override
            public ObservableList<TreeItem<String>> getSelectedItems() {
                return defaultSelectionModel.getSelectedItems();
            }

            @Override
            public void selectRange(int start, int end) {
                System.out.println("selectRange("+start+", "+end+")");
                List<TreeItem<String>> items = new ArrayList<>();
                for (int i = start; i < end; i++) {
                    items.add(tree.getTreeItem(i));
                }
                for (int i = start ; i > end; i--) {
                    items.add(tree.getTreeItem(i));
                }
                items.forEach(this::select);
            }

            @Override
            public void selectIndices(int index, int... indices) {
                System.out.println("select("+index+", "+Arrays.toString(indices)+")");
                TreeItem<String> item = tree.getTreeItem(index);
                if (item.isLeaf()) {
                    defaultSelectionModel.select(item);;
                } else {
                    List<TreeItem<String>> leaves = new ArrayList<>();
                    findLeavesAndExpand(item, leaves);
                    for (TreeItem<String> leaf : leaves) {
                        defaultSelectionModel.select(leaf);
                    }
                }
                for (int i : indices) {
                    item = tree.getTreeItem(i);
                    if (item.isLeaf()) {
                        defaultSelectionModel.select(item);;                        
                    } else {
                        List<TreeItem<String>> leaves = new ArrayList<>();
                        findLeavesAndExpand(item, leaves);
                        for (TreeItem<String> leaf : leaves) {
                            defaultSelectionModel.select(leaf);
                        }
                    }
                }
            }

            @Override
            public void selectAll() {
                System.out.println("selectAll()");
                List<TreeItem<String>> leaves = new ArrayList<>();
                findLeavesAndExpand(tree.getRoot(), leaves);
                for (TreeItem<String> leaf : leaves) {
                    defaultSelectionModel.select(leaf);
                }
            }

            @Override
            public void selectFirst() {
                System.out.println("selectFirst()");
                TreeItem<String> firstLeaf ;
                for (firstLeaf = tree.getRoot(); ! firstLeaf.isLeaf(); firstLeaf = firstLeaf.getChildren().get(0)) ;
                defaultSelectionModel.select(firstLeaf);
            }

            @Override
            public void selectLast() {
                System.out.println("selectLast()");
                TreeItem<String> lastLeaf ;
                for (lastLeaf = tree.getRoot(); ! lastLeaf.isLeaf(); 
                        lastLeaf = lastLeaf.getChildren().get(lastLeaf.getChildren().size()-1)) ;
                defaultSelectionModel.select(lastLeaf);
            }

            @Override
            public void clearAndSelect(int index) {
                TreeItem<String> item = tree.getTreeItem(index);
                defaultSelectionModel.clearSelection();
                if (item.isLeaf()) {
                    defaultSelectionModel.select(item);
                } else {
                    List<TreeItem<String>> leaves = new ArrayList<>();
                    findLeavesAndExpand(item, leaves);
                    for (TreeItem<String> leaf : leaves) {
                        defaultSelectionModel.select(leaf);
                    }
                }
            }

            @Override
            public void select(int index) {
                System.out.println("select("+index+")");
                select(tree.getTreeItem(index));
            }

            @Override
            public void select(TreeItem<String> item) {
                System.out.println("select("+item.getValue()+")");
                if (item.isLeaf()) {
                    defaultSelectionModel.select(item);
                } else {
                    List<TreeItem<String>> leaves = new ArrayList<>();
                    findLeavesAndExpand(item, leaves);
                    for (TreeItem<String> leaf : leaves) {
                        defaultSelectionModel.select(leaf);
                    }                    
                }
            }

            @Override
            public void clearSelection(int index) {
                defaultSelectionModel.clearSelection(index);
            }

            @Override
            public void clearSelection() {
                defaultSelectionModel.clearSelection();
            }

            @Override
            public boolean isSelected(int index) {
                return defaultSelectionModel.isSelected(index);
            }

            @Override
            public boolean isEmpty() {
                return defaultSelectionModel.isEmpty();
            }

            @Override
            public void selectPrevious() {
                // TODO Auto-generated method stub
                // not sure on implementation needed here
            }

            @Override
            public void selectNext() {
                System.out.println("selectNext()");
                // TODO Auto-generated method stub
                // not sure on implementation needed here
            }

            private void findLeavesAndExpand(TreeItem<String> node, List<TreeItem<String>> leaves) {
                if (node.isLeaf()) {
                    leaves.add(node);
                } else {
                    node.setExpanded(true);
                    for (TreeItem<String> child : node.getChildren()) {
                        findLeavesAndExpand(child, leaves);
                    }
                }
            }

        });

        primaryStage.setScene(new Scene(new BorderPane(tree), 400, 400));
        primaryStage.show();
    }

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


来源:https://stackoverflow.com/questions/33655663/javafx-treetable-select-children-when-parent-is-selected-and-remove-selection-fr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!