问题
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 ObservableList
s 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