Skip to Main Content

Java Programming

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

Jtree and TreeNode interface

807591Apr 27 2008 — edited May 2 2008
Hi,

I am trying to create a custom node (a node with a jlabel and a textfield) for a a JTree. I am not entirely sure how to accomplish this, so I a certain route. I tried to create a custom class that extends JLabel and implements TreeNode. Is this the way to do it? If it is, I can't compile because of override error. Look at the code:
public class Node extends JLabel implements TreeNode {
		
		private JLabel label;
		
		public Node () {
			label = new JLabel("TESTST");
		}
		
		public Enumeration children() { return null; }
		
		public boolean isLeaf() { return true; }
		
		public boolean getAllowsChildren() { return true; }
		
		public int getIndex(TreeNode node) { return 0; }
		
		public TreeNode getParent() { return null; }
		
		public int getChildCount() { return 0; }
		
		public TreeNode getChildAt(int childIndex) { return null; }
}
The problem is the getParent() method. This method is defined in java.awt.Component, and I can thus not override it because there is an incompatible returntype. Is there a way around this? Creating a custom tree is confusing.

Edited by: Siniz on Apr 27, 2008 4:47 AM

Comments

darrylburke
I tried to create a custom class that extends JLabel and implements TreeNode. Is this the way to do it?
If I understand what you are trying to achieve, no. I suggest you go through this tutorial:
The Java™ Tutorials: [How to Use Trees|http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html]
where you will find links to working code for using JTree.

db
807591
That is the actual tutorial I read that suggested (or at least hade a brief explanation) about it. It says I can implement my own TreeNodes.
807591
Don't extend JLabel. If you look at DefaultMutableTreeNode, you'll see that in addition to all the "tree stuff", it lets the user set and get an Object. That object is the "contents" of the node. I would suggest either using DMTN, or using the same design.

Also, if you want those components for rendering purposes, you should be using TreeNodeRenderer.

Finally, in the future, swing questions should be asked in the swing forum.
807591
I've tried that approach, if you mean TreeCellRenderer and not TreeNodeRenderer. The problem with that is that it's not possible to create custom nodes that for example contains a JTextField.
807591
Siniz wrote:
Hi,

I am trying to create a custom node (a node with a jlabel and a textfield) for a a JTree. I am not entirely sure how to accomplish this, so I a certain route. I tried to create a custom class that extends JLabel and implements TreeNode. Is this the way to do it?
No. You are confusing the view with the model.

[http://java.sun.com/products/jfc/tsc/articles/architecture/|http://java.sun.com/products/jfc/tsc/articles/architecture/]

Here's some example code. It doesn't do anything useful but does contain some of the basics for custom rendering and editing of JTree nodes.
import java.awt.Component;
import java.awt.GridLayout;
import java.util.Arrays;
import java.util.EventObject;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.WindowConstants;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultTreeCellEditor;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

public class CustomTreeExample {

    CustomTreeExample() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        JTree tree = new JTree();
        tree.setModel(new SimpleTreeModel());
        SimpleCellRenderer renderer = new SimpleCellRenderer();
        tree.setCellRenderer(renderer);
        tree.setCellEditor(new SimpleCellEditor(tree, renderer));
        tree.setEditable(true);
        f.setContentPane(new JScrollPane(tree));
        f.setSize(400, 300);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        new CustomTreeExample();

    }

    private static class SimpleTreeModel implements TreeModel {
        private Object root = "Root Node";
        private String[] nodes = { "Node A", "Node B", "Node C" };
        private String[] subNodes = { "Subnode One", "SubNode 2", "SubNode 3" };

        private transient EventListenerList listenerList = new EventListenerList();

        @Override
        public void addTreeModelListener(TreeModelListener tml) {
            listenerList.add(TreeModelListener.class, tml);

        }

        @Override
        public Object getChild(Object parent, int childIndex) {
            if (parent == root) {
                return nodes[childIndex];
            }
            List nodeList = Arrays.asList(nodes);
            if (nodeList.contains(parent)) {
                return subNodes[nodeList.indexOf(parent)];
            }
            // must be a subNode, which has no children
            return null;
        }

        @Override
        public int getChildCount(Object parent) {
            if (parent == root) {
                return 3;
            }
            if (Arrays.asList(nodes).contains(parent)) {
                return 1;
            }
            // must be a subNode, which has no children
            return 0;
        }

        @Override
        public int getIndexOfChild(Object parent, Object child) {
            if (parent == root) {
                return Arrays.asList(nodes).indexOf(child);
            }
            if (Arrays.asList(nodes).contains(parent)) {
                return Arrays.asList(subNodes).indexOf(child);
            }
            // must be a subNode, which has no children
            return -1;
        }

        @Override
        public Object getRoot() {
            return root;
        }

        @Override
        public boolean isLeaf(Object obj) {
            return Arrays.asList(subNodes).contains(obj);
        }

        @Override
        public void removeTreeModelListener(TreeModelListener tml) {
            listenerList.remove(TreeModelListener.class, tml);

        }

        @Override
        public void valueForPathChanged(TreePath arg0, Object arg1) {
            // no-op

        }

    }

    private static class SimpleCellRenderer extends DefaultTreeCellRenderer {
        private JPanel panel;
        private JTextField field;
        private JLabel label;

        SimpleCellRenderer() {
            panel = new JPanel(new GridLayout(1, 0));
            label = new JLabel();
            field = new JTextField();
            panel.add(label);
            panel.add(field);
        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                boolean leaf, int row, boolean hasFocus) {
            SimpleTreeModel stm = (SimpleTreeModel) tree.getModel();
            if (stm.isLeaf(value)) {
                label.setText("LEAF:");
            } else {
                if (value == stm.getRoot()) {
                    label.setText("ROOT:");
                } else {
                    label.setText("NODE:");
                }
            }
            field.setText(String.valueOf(value));
            return panel;
        }
    }
    
    private static class SimpleCellEditor extends DefaultTreeCellEditor {
        SimpleCellEditor(JTree tree, SimpleCellRenderer renderer) {
            super(tree, renderer);
        }

        @Override
        public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded,
                boolean leaf, int row) {
            // TODO Auto-generated method stub
            return renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, true);
        }
    }
}
807591
This is confusing to me. In what aspect am I confusing the model from the view. Also, your example isn't that easy to understand if you have never worked with the trees before and when you are new with Java.
807591
Siniz wrote:
This is confusing to me. In what aspect am I confusing the model from the view.
The 'model' is the data of the tree and the relationships between the data. This is what is represented by TreeModel, TreeNode, DefaultMutableTreeNode, etc.

The 'view' is the display of the model data.

You should not mix the these two things. A JLabel that is also a TreeNode would violate this concept of separation.

Also, your example isn't that easy to understand if you have never worked with the trees before and when you are new with Java.
Custom TreeCellRenderers and TreeCellEditors aren't trivial topics.

My SimpleTreeModel is exactly that, a simplistic implementation of TreeModel for the purpose of the example. It is of little use in real code. When creating trees in Java, using DefaultMutableTreeNodes and DefaultTreeModel is not the norm. Most developers will create their own custom TreeModel implementation that is closely aligned with the data they want to represent.

The SimpleCellRenderer class replaces the typical JLabel returned by DefaultTreeCellRenderer with a JPanel containing a JLabel and a JTextField. This class is missing all sorts of features of the DefaultTreeCellRenderer. The selected row isn't highlighted, the focus isn't painted, etc. To create a 'proper' implementation would require even more code.

The SimpleCellEditor borrows the code from the SimpleCellRenderer to present an editable version of the JPanel returned by the renderer. It is missing features as well, such as the code associated with updating the model with the new value after editing.

If you are looking for a simple solution to your problem, there isn't one.

Jim S.
807591
That was some really good info Niceguy. It helped a lot. I understand JTree is advanced and kept all the code I managed to produce, eventhough I ultimately decided to for a simple setVisible() approach on some panels instead. This is because I only have two levels. Thanks.
1 - 8
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on May 30 2008
Added on Apr 27 2008
8 comments
247 views