使用JavaFX中的treeItem获取treeCell实现

时间:2014-05-06 09:21:26

标签: javafx javafx-2 javafx-8

我正在两个树视图之间实现拖动和拖动。当treeItem被拖放到treeItem的另一个treeView时,在两个treeItem之间建立了一个线连接。这工作正常,但最初没有拖放事件的连接对我来说是个问题。     我正在使用treeCell进行拖放事件。

3 个答案:

答案 0 :(得分:3)

正如我所想,事实证明这很困难。

根据设计,无法让TreeCell属于给定的TreeItem。这是因为"虚拟化" TreeView的设计:创建最少数量的TreeCell,并根据需要使用新的TreeItem更新这些TreeCell。因此,对于每个TreeItem,通常不会是TreeItem,而给定TreeCell所代表的ObservableSet可能会随时发生变化。

为了完成这项工作,首先要创建一个可观察的集合,存储树之间的所有连接(例如,AssignmentView应该可以正常工作)。通过某个类来表示连接,该类公开可用于行的起点和终点。

为树创建自定义单元工厂。他们返回的细胞应该:

  1. 观察他们所代表的项目。如果项目更改为位于一个或多个连接的末尾的项目,则应将这些连接上的相应点绑定到单元格坐标的相应变换。
  2. 如果项目从更改了一个或多个连接末尾的,则从单元格坐标中取消绑定相应的结尾
  3. 观察可观察的连接集合。如果添加了一个此单元格的项目是一端,则绑定上面的坐标
  4. 请注意,绑定坐标时,需要考虑单元格可能会移动的事实(例如,通过滚动或GUI布局中的其他更改)。您还需要将坐标从单元格自己的坐标系转换为坐标系统,无论哪个窗格连接到另一个窗格,显然,它们必须是两个常见的场景图的祖先。树)。

    最后,你需要一些家务。连接需要确保它们变得不可见,或者如果它们不再绑定在一端或多端,则从场景中删除。

    我创建了an example。我刚刚创建了一些用于生成连接的简单控件,但您可以通过拖放轻松完成此操作。封装连接视图的类是Assignment;它使用ConnectedTrees来表示连接的实际数据。 {{1}}类是主要的应用程序,大多数有趣的控制器类型的工作都在那里。其余的类只是数据表示。这个例子都是Java 8;我认为在JavaFX 2.2中它会更加丑陋。

答案 1 :(得分:2)

Set<Node> treeCells = tree.lookupAll(".tree-cell");
List<Node> cells = new ArrayList<>(treeCells);
int row = tree.getRow(((TreeItem) treeItem));
TreeCell cell= ((TreeCell) cells.get(row))

答案 2 :(得分:0)

此解决方案使用递归调用来遍历树视图的节点树。 通常这种递归不应该是危险的,因为只有有限的节点数(TreeCell的实例被TreeView重用):

import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;

import java.util.List;

public class TreeViewExplore {

    /**
     * Returns a cell for the tree item given. Or null.
     *
     * @param treeItem tree item
     * @param treeView tree view
     * @return tree cell
     */
    public static TreeCell findCellByItem(TreeItem treeItem, TreeView treeView) {
        return recursiveFindCellByItem(treeItem, treeView);
    }

    private static TreeCell recursiveFindCellByItem(TreeItem treeItem, Node node) {

        if (node.getStyleClass().contains("tree-cell")
                && TreeCell.class.isAssignableFrom(node.getClass())
                && ((TreeCell) node).getTreeItem() == treeItem)
            return (TreeCell) node;

        if (!Parent.class.isAssignableFrom(node.getClass()))
            return null;

        List<Node> nodes = ((Parent) node).getChildrenUnmodifiable();
        if (nodes == null) return null;

        for (Node n : nodes) {
            TreeCell cell = recursiveFindCellByItem(treeItem, n);
            if (cell != null) return cell;
        }
        return null;
    }
}

用法:

// TreeItem treeItem = ...

TreeCell cell = TreeViewExplore.findCellByItem(treeItem, treeView);

// Check result:
System.out.println(
        cell == null ? "cell is null" :
                "(cell.getTreeItem() == treeItem) = "
                        + (cell.getTreeItem() == treeItem));

使用lookupAll()方法的另一种解决方案。这只是在这里,因为它看起来对我来说非常有效,因为这个方法收集所有给定CSS选择器的节点(并且在任何情况下遍历整个树):

public static TreeCell findCellByItem(TreeItem treeItem, TreeView treeView) {
    return (TreeCell) treeView.lookupAll(".tree-cell").stream()
            .filter(n -> ((TreeCell) n).getTreeItem() == treeItem)
            .findFirst()
            .orElse(null);
}