Java – Help making a JTree with a JCheckBox

checkboxjavajtreeswing

I have an unusual situation where I need to have a JTree with each node containing 2 checkboxes and a label (with the ability to add a listener to tell when any of the potential checkboxes are checked). I also need the root node to have the same layout (which I'm assuming means creating a JPanel with 2 JCheckBoxes and a JLabel), with the ability to select all the checkboxes down the tree if one in the root is checked.

Any guidance or examples? I've checked out previous questions on here and associated examples…some of which allowed me to get to the point of having the tree "look" but without giving me a direction for implementing the action behind it.

Thanks!

Best Solution

This might be a good time to look at the old JTreeTable code, which will give you a tree rendered in the first column, and the freedom to render the cells for each column to the right of the tree node as you wish, in your case putting in checkboxes and a label, and allowing you to have TableCellEditors working with your JTable as you are used to. A warning is that, while the code in that link works, it is a little convoluted.

There is an alternative. I have demoed below a Tree Table implementation that is supposed to be better, called Outline, provided by NetBeans (though you don't need to develop with the NetBeans IDE, you just need the jar). This article indicates how easy it is to be to get started.

I was able to mock up a quick example of the Outline tree table in Eclipse (with the org-netbeans-swing-outline.jar imported to my project) in about 30 minutes (I am slow at typing):

private void buildFrame() {
    frame = new JFrame("Demo");
    frame.setSize(300, 300);
    addStuffToFrame();
    frame.setVisible(true);

}


private void addStuffToFrame() {
    MyTreeNode top = new MyTreeNode("top");
    createNodes(top);
    DefaultTreeModel model = new DefaultTreeModel(top);     
    //here are the netBeans tree table classes 
    OutlineModel outlineModel = 
             DefaultOutlineModel.createOutlineModel(model, new MyRowModel());
    Outline outline = new Outline();
    outline.setRootVisible(true);
    outline.setModel(outlineModel);
    frame.getContentPane().add(new JScrollPane(outline));
}

private void createNodes(MyTreeNode top) {
    MyTreeNode child = new MyTreeNode("child 2");
    top.add(new MyTreeNode("child 1"));
    child.add(new MyTreeNode("g-child1"));
    child.add(new MyTreeNode("g-child2"));
    child.add(new MyTreeNode("g-child3"));
    top.add(child);
    top.add(new MyTreeNode("child3"));
    top.add(new MyTreeNode("child4"));

}

I create a TreeNode to hold the Booleans that will interoperate well with the JTable's built-in checkbox rendering mechnanism.

public class MyTreeNode extends DefaultMutableTreeNode {
    Boolean data1 = null;
    Boolean data2 = null;
    String name = null;
    MyTreeNode (String name) {
        this.name=name;
    }

    void setData1(Boolean val) {data1=val;}
    void setData2(Boolean val) {data2=val;}
    Boolean getData1() {return data1;}
    Boolean getData2() {return data2;}
    String getName() {return name;}

}

The netBeans RowModel is the key to making this a table instead of a simple JTree:

public class MyRowModel implements RowModel {

    public Class getColumnClass(int col) {
        switch (col) {
        case 0: return String.class;
        case 1: return Boolean.class; //these return class definitions will
        case 2: return Boolean.class; //trigger the checkbox rendering
        default:return null;    
        }
    }

    public int getColumnCount() {
        return 3;
    }

    public String getColumnName(int col) {
        return "";
    }

    public Object getValueFor(Object node, int col) {
        MyTreeNode n = (MyTreeNode)node;
        switch (col) {
        case 0: return n.getName();
        case 1: return n.getData1();
        case 2: return n.getData2();
        default:return null;
        }
    }

    public boolean isCellEditable(Object node, int col) {
        return col > 0;
    }

    public void setValueFor(Object node, int col, Object val) {
        MyTreeNode n = (MyTreeNode)node;
        if (col == 1)      {n.setData1((Boolean)val);}
        else if (col == 2) {n.setData2((Boolean)val);}
        //EDIT:  here is a recursive method to set all children
        //       selected for one of the two checkboxes as it is 
        //       checked by the parent
        for (Enumeration children = n.children();
                       children.hasMoreElements(); ) {
            MyTreeNode child = (MyTreeNode) children.nextElement();
            setValueFor(child, col, val);
        }


    }

}

here is the finished, albeit simplistic, product:

alt text http://img17.imageshack.us/img17/6643/picture1hz.png

I have updated the setValueFor method to iterate over a node's children and set the checkboxes as selected or deselected when a parent has been modified.

Related Question