문제

I am implementing drag and drag between two tree views. When a treeItem is dropped onto another treeView of treeItem a line connection is established between the two treeItems. This is working fine , but to have a connection initially without a drag and drop events is problem to me. I am using treeCell for the drag and drop events.

도움이 되었습니까?

해결책 2

As I thought, this turns out to be pretty difficult.

There is, by design, no way to get the TreeCell belonging to a given TreeItem. This is because of the "virtualization" design of the TreeView: a minimal number of TreeCells are created and these are updated with new TreeItems as required. Thus there typically won't be a TreeCell for every TreeItem, and the TreeItem represented by a given TreeCell may change at any point.

To make this work, first create an observable collection storing all the connections between the trees (e.g. an ObservableSet should work well). Represent the connections by some class that exposes start and end points which can be used for the lines.

Create custom cell factories for the trees. The cells they return should:

  1. observe the item they are representing. If the item changes to one that is at an end of one or more connections, then the appropriate point on those connections should be bound to the appropriate transform of the coordinates of the cell.
  2. If the item changes from one that is at the end of one or more connections, then unbind the appropriate end from the cell coordinates
  3. observe the observable collection of connections. If one is added for which this cell's item is one end, then bind the coordinates as above

Note that when you bind the coordinates, you need to take into account the fact that the cells may move (e.g. via scrolling or via other changes in GUI layout). You also need to transform the coordinates from the cell's own coordinate system into the coordinate system of whichever pane is holding the connections (obviously, if these are connecting one tree to another, it must be some common scene graph ancestor of both trees).

And finally, you need some housekeeping. The connections need to make sure they either become invisible, or are removed from the scene if they are no longer bound at one or more ends.

I created an example. I just created some simple controls for generating the connections, but you could easily do this with drag and drop instead. The class encapsulating the view of the connection is AssignmentView; it uses Assignment to represent the actual data that is connected. The ConnectedTrees class is the main application and most of the interesting controller-type work is in there. The remaining classes are just data representation. The example is all Java 8; I think it would be much uglier in JavaFX 2.2.

다른 팁

final var treeCells = treeView.lookupAll( ".tree-cell" );
final var cells = new ArrayList<>( treeCells );
final var row = treeView.getRow( n );
final var node = cells.get( row );
if( node instanceof TreeCell ) {
  @SuppressWarnings("rawtypes")
  final var cell = (TreeCell) node;
  System.out.println( "TREE CELL: " + cell );
}

This solution uses recursion calls to traverse the nodes tree of the tree view. Normally this recursion shouldn't be dangerous as there is only limited nodes number (TreeCell's instances are reused by 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;
    }
}

Usage:

// TreeItem treeItem = ...

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

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

Yet another solution using lookupAll() method. It is only for example here as it looks non very efficient for me because this method collects all nodes with CSS-selector given (and traverses all over the tree in any case):

public static TreeCell findCellByItem(TreeItem treeItem, TreeView treeView) {
    return (TreeCell) treeView.lookupAll(".tree-cell").stream()
            .filter(n -> ((TreeCell) n).getTreeItem() == treeItem)
            .findFirst()
            .orElse(null);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top