Results 1 to 9 of 9
- 11-01-2009, 12:51 PM #1
Member
- Join Date
- Nov 2009
- Posts
- 5
- Rep Power
- 0
Multiple components as one node in JTree
Hi there,
I want to customize the nodes of a JTree. I've seen lots and lots of examples how to modify JTables and how to add checkboxes to JTrees (incredibly popular as it seems) but that's not enough for my problem. Getting a model to represent the structure of my data is not hard by deriving a custom class from DefaultMutableTreeNode but once I connect the model to the JTree all it renders is the custom class' toString() method which I can of course override.
Say every node in the tree (rather in the model) relates to one custom object called TCustomRow. Visually a TCustomRow shall be represented by (from left to right) a button, a label and a textfield. Actually I need more components then that but I want to understand how to do it so I can extend it any time I need to.
I've read through some related material and it seems as if I had to create my own cell renderer and my own cell editor which kind of scares me. Is there another - more intuitive - way ?
Thanks in advance,
Amnu
- 11-01-2009, 06:47 PM #2
Senior Member
- Join Date
- Aug 2009
- Location
- Pittsburgh, PA
- Posts
- 282
- Rep Power
- 4
What you would seem to want is some way to create a JPanel for each tree node.create my own cell renderer and my own cell editor which kind of scares me.
Is there another - more intuitive - way ?
Well, that is exactly what you get with a TreeDellRenderer.
How would you design something more intuitive?
(Examples generally use super.getTreeCellRenderer because that returns a JLabel.Java Code:public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { JPanel val = new JPanel(); ... put things into the JPanel ... return JPanel; }
And a JLabel is easier to use than a JPanel; but less flexible.
I have not yet needed a TreeCellEditor.
I think it diffrers from a TreeCellRendeer in that it can get keystrokes and mouse hits.
If so, you may need a TreeCellEditor instead of a TreeCellRenderer.
- 11-02-2009, 11:30 AM #3
Member
- Join Date
- Nov 2009
- Posts
- 5
- Rep Power
- 0
I'll write down the way I think it should be done. Please correct me where I'm going wrong.
Implement interface javax.swing.tree TreeCellRenderer (e.g. subclassing javax.swing.tree.DefaultTreeCellRenderer) to display arbitrary contents in a JTree. For example, I can create a custom component extending JPanel, set this up in a GUI designer and simply use its design when instantiating during construction time of my custom TreeCellRenderer. This is a simple approach:
Then I only need to doJava Code:import java.awt.Component; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; public class TCustomNodeRenderer extends DefaultTreeCellRenderer { TCustomRow m_customRow; public TCustomNodeRenderer() { m_customRow = new TCustomRow (); } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { return m_customRow ; } }
and all the nodes in the tree will be displayed using TCustomRow's visual representation. Now any changes I commit to that one instance of TCustomRow are immidiately shown in the Tree. So far so good. Now I want to be able to actually edit the rows, e.g. by using the JTextField and JSpinner components existing inside a TCustomRow. This is where I get lost...Java Code:TCustomNodeRenderer nodeRender = new TCustomNodeRenderer(); jTree1.setCellRenderer(nodeRender);
I can implement javax.swing.tree.TreeCellEditor (e.g. by subclassing javax.swing.DefaultTreeCellEditor) and try to use it to edit my rows - but how ? I understand I have to do
but what then ? How do I make the tree call the custom editor ? How can I make the displayed TCustomRow editable so the user can enter values into the JTextField and JSpinner components ? This is what I do not understand.Java Code:TCustomNodeEditor nodeEditor = new TCustomNodeEditor (); jTree1.setCellEditor(nodeEditor );
Amnu
edit:
I just found the solution. For now I have no more questions but there sure are more to come..Last edited by Amnuriak; 11-02-2009 at 12:54 PM.
- 11-02-2009, 02:15 PM #4
Member
- Join Date
- Nov 2009
- Posts
- 5
- Rep Power
- 0
I'm confused - yet again.
I have two custom classes
andJava Code:public class TCustomNodeRenderer extends DefaultTreeCellRenderer
The renderer's getTreeCellRendererComponent() returns a TCustomRow object subclassing JPanel which holds several other components (such as a JTextField and a JSpinner). These are displayed correctly in the corresponding JTree. To do this I instantiate the TCustomRow class durint construction time of the custom renderer, store it in a field and return it when needed. I might just as well have it set from the outside - nothing to worry about right now.Java Code:public class TCustomNodeEditor extends DefaultTreeCellEditor
The editor takes the renderer as constructor argument and returns the renderer's TCustomRow object when the JTree object calls its getTreeCellEditorComponent(). So the visual representation stays correct all the time.
The tree I'm working with holds numerous nodes (up to 3 levels deep) which shall be constructed and added during run time. Each of these nodes shall have its own TCustomRow object do display its values (each node has several distinct values). For these values there are different components in the TCustomRow class, such as a JTextField and a JSpinner as mentioned above.
How would I set this tree up ? I need the renderer and the editor to be able to render and edit each row depending on the values of its corresponding node in the tree model. How do I build this connection/relation ? How do I extract the information which node is selected from the tree, pass it to the renderer and have it edita and display the right TCustomRow object ?
If I simply change the TCustomRow object for the renderer it will change all the tree nodes' visual representations.
Edit:
Once again I find the solution right after I post my request .... The solution to this problem is the second paramater on the getTreeCellEditorComponent() / getTreeCellRendererComponent() methods. It corresponds to the node in the tree's model which is being querried. So that node can pass data to the renderer and the editor for custom display purposes, e.g. through the toString() function. I rather not encode my custom node data into a string so I can pass it out of the node via toString() and into the renderer and editor by decoding the passed string into its data components but at least that's a possibility. That kind of data tunneling via serialization is just not the kind of programming considered "clean".Last edited by Amnuriak; 11-02-2009 at 02:39 PM.
- 11-02-2009, 03:25 PM #5
Senior Member
- Join Date
- Aug 2009
- Location
- Pittsburgh, PA
- Posts
- 282
- Rep Power
- 4
Let us suppose that your tree is a tree of MyTreeObject objects.
The treeModel will return a MyTreeObject from myJTree.getModel().getRoot()
or myJTree.getModel().getChild(). Here's the trick:
the second parameter to getTreeCellRendererComponentSo the body of CutomTreeCellRenderer can reliably cast that argument to MyTreeObject.
IS an element from the tree model.
There is a different TreeCellRenderer object for every tree cell.Java Code:public class CustomTreeCellRenderer extends JPanel { public CustomTreeCellRenderer (JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { MyTreeObject row = [B][COLOR="DarkGreen"](MyTreeObject)[/COLOR][/B]value; // cast value to its actual type // fetch and display items from row } } public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { return new CustomTreeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus); }
It may be helpful to imagine that getTreeCellRendererComponent
is called every time a cell is to be displayed.
- 11-03-2009, 01:36 PM #6
Member
- Join Date
- Nov 2009
- Posts
- 5
- Rep Power
- 0
Thanks for the help :-)
It's working quite well and I'm making progress. With CustomTreeCellEditor there still is one thing bugging me though:
I want to be able to cause cell selection to trigger cell editing. Imho direct approach is to have canEditImmidiately() return true once the event is a MouseEvent and getClickCount() returns 1 but it causes some weird behaviour: The rows will disappear once in a while (depending on where the user clicks).
I can't figure out why this happens. Some hint please..
Amnu
Edit:
Another problem:
I want to hide the root of my model so I set up the tree accordingly. I also wish to be able to expand the visual nodes only when the user clicks the icons in front of the nodes (default little "plus" signs). I don't know where/how to do this.
When hiding the root of the tree the 1st level nodes don't have their icons displayed anymore, meaning the user can only expand them by double clicking the row - which I do not want. So the icons (default little "plus" signs) have to stay visible and accessable all the time. I don't know how to tell the tree to keep the icons visible.Last edited by Amnuriak; 11-03-2009 at 04:16 PM.
- 11-03-2009, 04:24 PM #7
Senior Member
- Join Date
- Aug 2009
- Location
- Pittsburgh, PA
- Posts
- 282
- Rep Power
- 4
"rows will disappear"
Are they blank or is the rest of the text moved up?
Are they still in the tree, or is the data modified?
If the area becomes blank and the data is still in the tree,
the problem may be some race condition
among the various pieces of code listeneing for mouse events on the cell.
I imagine that starting the editor means replacing the screen image
formed by the TreeCellRenderer with one formed by the TreeCellEditor.
This process could conceivably be halted midway with neither visible.
- 11-04-2009, 01:43 PM #8
Member
- Join Date
- Nov 2009
- Posts
- 5
- Rep Power
- 0
I was able to track down the problem to this: focus gain and loss.
I assume it is a problem with my CellEditor and CellRenderer:
Is this not the way one should extend these classes ? Apparently they are supposed to be used slightly differently but I cannot figure out how.Java Code:public class TCustomNodeRenderer extends DefaultTreeCellRenderer { TCustomRow m_customRow; public TCustomNodeRenderer(TTcbGui tcbGui) { m_customRow = new TCustomRow(tcbGui); } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { return getCustomRow(value, tree.isEditable()); } public TSkillRow getCustomRow(Object value, boolean enableMode) { if (value instanceof TCustomNode) { TCustomRow customNode = (TCustomNode)value; m_customRow.setEnabled(enableMode); } return m_customRow; } } public class TCustomNodeEditor extends DefaultTreeCellEditor { TCustomNodeRenderer m_renderer; public TCustomNodeEditor(JTree tree, DefaultTreeCellRenderer renderer) { super(tree, renderer); m_renderer = (TCustomNodeRenderer)renderer; } @Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { return m_renderer.getCustomRow(value, tree.isEditable()); } }
The problems only happen if the user switches focus to some other component. I can assert this visually by painting the currently selected row with some color. Once the user moves the mouse and clicks on another row all the rows in between (including the row selected before and the newly selected row) become colored as well. So this clearly is some focus gain/loss problem.
I thought about implementing the mouse listener interface but this can't be right. The CellEditor has to have a way of realizing focus gain/loss and thus also of starting/stopping editing at the right time. After all, other applications do not suffer this issue ...
My best guess is to use this "realEditor" thing mentioned in the documentation of DefaultTreeCellEditor but to be honest: I have no idea what that is and how it relates to the CellEditor.Last edited by Amnuriak; 11-04-2009 at 01:50 PM.
- 11-04-2009, 02:47 PM #9
Senior Member
- Join Date
- Aug 2009
- Location
- Pittsburgh, PA
- Posts
- 282
- Rep Power
- 4
The advice above was too well hidden, so let me repeat it.
By ignoring that advice, the code in the previous post is much too elaborate.There is a different TreeCellRenderer object for every tree cell.
The equivalent "corrected" code would create a new Renderer for each cell.
It might look like this.
NotesJava Code:public class TCustomNodeRenderer extends DefaultTreeCellRenderer { TTcbGui tcbGui; public TCustomNodeRenderer(TTcbGui t) { tcbGui = t; } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { TCustomRow m_customRow = new TCustomRow(tcbGui); m_customRow.setEnabled(tree.isEditable()); return m_customRow; } }
- The code in the previous post extracts (TCustomNode)value and then ignores it.
(So how does the TCustomRow get the value???) - All the other fine info passed to getTreeCellRendererComponent is also ignored.
- This code may be "corrected", but is not correct.
Similar Threads
-
JTree Programmatic Node Expansion and Selection Probelm
By hemanthjava in forum AWT / SwingReplies: 3Last Post: 01-16-2013, 07:23 AM -
Display tooltip information when a node from JTree is clicked
By javanewbie in forum AWT / SwingReplies: 1Last Post: 06-22-2009, 02:39 AM -
How to add JCheckbox as a node in JTree
By shajuantony in forum javax.swingReplies: 0Last Post: 04-09-2009, 07:19 AM -
JSplitPane Arranging Components(JTree) Doubt
By hemanthjava in forum AWT / SwingReplies: 0Last Post: 08-03-2008, 08:29 AM -
Node selection in jtree
By Preethi in forum AWT / SwingReplies: 4Last Post: 06-19-2008, 11:25 PM


LinkBack URL
About LinkBacks
Reply With Quote
Bookmarks