简体   繁体   中英

How to create a hierarchical tree with collapsible nodes in JGraphX

I need an org chart tree and I want to be able to collapse and expand the nodes at any level. I am new to JGraphX but from what I have read it sounds like the way to implement folding is to group the vertexes. The problem is when I create the group it puts all the child vertices inside the parent vertex.

Here is some example code that gives a great layout but does not support folding:

package com.mxgraph.examples.swing;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.SwingConstants;

import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
public class HelloWorld extends JFrame
{

    /**
     * 
     */
    private static final long serialVersionUID = -2707712944901661771L;

    public HelloWorld()
    {
        super("Hello, puppies!");

        mxGraph graph = new mxGraph();
        Object parent = graph.getDefaultParent();

        graph.getModel().beginUpdate();
        try
        {
            //Notice that the parent is the default parent... 
            //The hierarchical structure looks great but I cannot collapse/expand the tree.
            Object vDogsRoot = graph.insertVertex(parent, null, "DOG", 0, 0, 80, 30);
            Object v2 = graph.insertVertex(parent, null, "Shar Pei", 0, 0, 80, 30);
            Object v3 = graph.insertVertex(parent, null, "Pug", 0, 0, 80, 30);
            Object v4 = graph.insertVertex(parent, null, "Cocker Spaniel", 0, 0, 80, 30);
            Object v5 = graph.insertVertex(parent, null, "Pit Bull", 0, 0, 80, 30);
            Object v6 = graph.insertVertex(parent, null, "Chihuahua", 0, 0, 80, 30);

            graph.insertEdge(parent, null, "", vDogsRoot, v2);
            graph.insertEdge(parent, null, "", vDogsRoot, v3);
            graph.insertEdge(parent, null, "", vDogsRoot, v4);
            graph.insertEdge(parent, null, "", vDogsRoot, v5);
            graph.insertEdge(parent, null, "", vDogsRoot, v6);

            mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
            layout.setUseBoundingBox(false);

            layout.execute(parent);
        }
        finally
        {
            graph.getModel().endUpdate();
        }

        mxGraphComponent graphComponent = new mxGraphComponent(graph);
        getContentPane().add(graphComponent);
    }

    public static void main(String[] args)
    {
        HelloWorld frame = new HelloWorld();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 320);
        frame.setVisible(true);
    }

}

Produces:

第一个代码产生这个图像

Great start but no collapse button. The following code demonstrates the problem I am having. To support folding I attempt to create a group by changing the parent of the vertices from the default parent to the vDogVertex which is the root of the tree. This becomes collapsible, however all of the child vertices are inside of the vDogVertex and this ruins the trees layout.

package com.mxgraph.examples.swing;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.SwingConstants;

import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.view.mxGraph;
import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
public class HelloWorld extends JFrame
{

    /**
     * 
     */
    private static final long serialVersionUID = -2707712944901661771L;

    public HelloWorld()
    {
        super("Hello, puppies!");

        mxGraph graph = new mxGraph();
        Object parent = graph.getDefaultParent();

        graph.getModel().beginUpdate();
        try
        {
            //Notice this time the parent is the vDogsRoot vertex. 
            //This creates a cell group if I understand correctly.
            Object vDogsRoot = graph.insertVertex(parent, null, "DOG", 0, 0, 80, 30, "");
            Object v2 = graph.insertVertex(vDogsRoot, null, "Shar Pei", 0, 0, 80, 30, "");
            Object v3 = graph.insertVertex(vDogsRoot, null, "Pug", 0, 0, 80, 30, "");
            Object v4 = graph.insertVertex(vDogsRoot, null, "Cocker Spaniel", 0, 0, 80, 30, "");
            Object v5 = graph.insertVertex(vDogsRoot, null, "Pit Bull", 0, 0, 80, 30, "");
            Object v6 = graph.insertVertex(vDogsRoot, null, "Chihuahua", 0, 0, 80, 30, "");

            graph.insertEdge(parent, null, "", vDogsRoot, v2);
            graph.insertEdge(parent, null, "", vDogsRoot, v3);
            graph.insertEdge(parent, null, "", vDogsRoot, v4);
            graph.insertEdge(parent, null, "", vDogsRoot, v5);
            graph.insertEdge(parent, null, "", vDogsRoot, v6);

            mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
            layout.setUseBoundingBox(false);

            layout.execute(vDogsRoot);  //apply the layout to the root group node.
            layout.execute(parent);
        }
        finally
        {
            graph.getModel().endUpdate();
        }

        mxGraphComponent graphComponent = new mxGraphComponent(graph);
        getContentPane().add(graphComponent);
    }

    public static void main(String[] args)
    {
        HelloWorld frame = new HelloWorld();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 320);
        frame.setVisible(true);
    }

}

Produces: (notice the collapse button)

在此处输入图片说明

How do I prevent the vertices from being inside the parent cell of the cell group? I want the tree hierarchy to be maintained but collapsible. Am I on the correct path using Cell Groups? What I am doing wrong?

I suspect I just need to tell the parent of the cell group (vDogsRoot) to allow cells to be draw outside its boundary but I do not see a way to do this yet. Or maybe I am taking a totally wrong approach. Thinking this should be a trivial thing to accomplish yet I have tried many different things and googled/read many docs and no success yet.

UPDATE 1:

Groups are not what I am needing here. I just need to traverse the directed tree and toggle showing the nodes below the selected node. Found a java script example named tree.html in the mxGraph examples folder. Just need to convert that example from JavaScript to Java.

Yikes, not sure how I feel about this but here is a solution. I partially converted the tree.html example from JavaScript to Java. I did not convert all of the code to Java; I didn't even try because I do not care about the location of the collapse button. I did get the functionality I desired. I did add a few codes that the super method was performing in case that portion is important to keep.

If anyone wants to finish converting the example and provide that to me I will gladly award you the answer instead of awarding myself the answer. Other ways to get awarded the answer - point out a bug in my code, improve it in someway or provide a better approach with code.

Here is my Java code:

package com.mxgraph.examples.swing;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;

import com.mxgraph.layout.mxCompactTreeLayout;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.view.mxGraph;

/**
 * A foldable directed acyclic graph (DAG) where each child has only one parent. AKA a Tree.
 * 
 * @author some programmer
 *
 */
class FoldableTree extends mxGraph
{
    /**
     * Need to add some conditions that will get us the expand/collapse icon on the vertex.
     */
    @Override
    public boolean isCellFoldable(Object cell, boolean collapse)
    {
        //I want to keep the original behavior for groups in case I use a group someday.
        boolean result = super.isCellFoldable(cell, collapse);
        if(!result)
        {
            //I also want cells with outgoing edges to be foldable...
            return this.getOutgoingEdges(cell).length > 0;
        }
        return result;
    }

    /**
     * Need to define how to fold cells for our DAG. In this case we want to traverse the tree collecting
     * all child vertices and then hide/show them and their edges as needed. 
     */
    @Override
    public Object[] foldCells(boolean collapse, boolean recurse, Object[] cells, boolean checkFoldable)
    {
        //super.foldCells does this so I will too...
        if(cells == null)
        {
            cells = getFoldableCells(getSelectionCells(), collapse);
        }

        this.getModel().beginUpdate();

        try
        {           
            toggleSubtree(this, cells[0], !collapse);
            this.model.setCollapsed(cells[0], collapse);
            fireEvent(new mxEventObject(mxEvent.FOLD_CELLS, "cells", cells, "collapse", collapse, "recurse", recurse));
        }
        finally
        {
            this.getModel().endUpdate();
        }

        return cells;
    }

    // Updates the visible state of a given subtree taking into
    // account the collapsed state of the traversed branches
    private void toggleSubtree(mxGraph graph, Object cellSelected, boolean show)
    {
        List<Object> cellsAffected = new ArrayList<>();
        graph.traverse(cellSelected, true, new mxICellVisitor() {                   
                    @Override
                    public boolean visit(Object vertex, Object edge) {
                        // We do not want to hide/show the vertex that was clicked by the user to do not
                        // add it to the list of cells affected.
                        if(vertex != cellSelected)
                        {
                            cellsAffected.add(vertex);
                        }

                        // Do not stop recursing when vertex is the cell the user clicked. Need to keep
                        // going because this may be an expand.
                        // Do stop recursing when the vertex is already collapsed.
                        return vertex == cellSelected || !graph.isCellCollapsed(vertex); 
                    }
                });

        graph.toggleCells(show, cellsAffected.toArray(), true/*includeEdges*/);     
    }
}

public class ChampsTree extends JFrame
{
    private static final long serialVersionUID = -2707712944901661771L;

    public ChampsTree()
    {
        super("Hello, World!");

        FoldableTree graph = new FoldableTree();

        mxCompactTreeLayout layout = new mxCompactTreeLayout(graph, false);         
        layout.setUseBoundingBox(false);
        layout.setEdgeRouting(false);
        layout.setLevelDistance(30);
        layout.setNodeDistance(10);

        Object parent = graph.getDefaultParent();

        graph.getModel().beginUpdate();
        try
        {           
            Object root = graph.insertVertex(parent, "treeRoot", "Root", 0, 0, 60, 40);

            Object v1 = graph.insertVertex(parent, "v1", "Child 1", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", root, v1);

            Object v2 = graph.insertVertex(parent, "v2", "Child 2", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", root, v2);

            Object v3 = graph.insertVertex(parent, "v3", "Child 3", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", root, v3);

            Object v11 = graph.insertVertex(parent, "v11", "Child 1.1", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v1, v11);

            Object v12 = graph.insertVertex(parent, "v12", "Child 1.2", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v1, v12);

            Object v21 = graph.insertVertex(parent, "v21", "Child 2.1", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v2, v21);

            Object v22 = graph.insertVertex(parent, "v22", "Child 2.2", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v2, v22);

            Object v221 = graph.insertVertex(parent, "v221", "Child 2.2.1", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v22, v221);

            Object v222 = graph.insertVertex(parent, "v222", "Child 2.2.2", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v22, v222);

            Object v31 = graph.insertVertex(parent, "v31", "Child 3.1", 0, 0, 60, 40);
            graph.insertEdge(parent, null, "", v3, v31);            

            layout.execute(parent);         
        }
        finally
        {
            graph.getModel().endUpdate();
        }

        graph.addListener(mxEvent.FOLD_CELLS,  new mxIEventListener() {

            @Override
            public void invoke(Object sender, mxEventObject evt) {
                layout.execute(graph.getDefaultParent());
            }
        });

        mxGraphComponent graphComponent = new mxGraphComponent(graph);

        getContentPane().add(graphComponent);
    }

    public static void main(String[] args)
    {
        ChampsTree frame = new ChampsTree();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 320);
        frame.setVisible(true);
    }
}

全面展开

Notice the tree becomes more compact (the remaining nodes come closer together) when collapsed. This is because of the handler that invokes the layout after fold occurs. This reduces the visual complexity of the tree which is good because my real tree will be pretty large.

在此处输入图片说明

在此处输入图片说明

完全折叠

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM