Results 1 to 9 of 9
  1. #1
    Amnuriak is offline Member
    Join Date
    Nov 2009
    Posts
    5
    Rep Power
    0

    Default 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

  2. #2
    zweibieren is offline Senior Member
    Join Date
    Aug 2009
    Location
    Pittsburgh, PA
    Posts
    284
    Rep Power
    5

    Default

    create my own cell renderer and my own cell editor which kind of scares me.
    Is there another - more intuitive - way ?
    What you would seem to want is some way to create a JPanel for each tree node.
    Well, that is exactly what you get with a TreeDellRenderer.
    How would you design something more intuitive?
    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;
    }
    (Examples generally use super.getTreeCellRenderer because that returns a JLabel.
    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.

  3. #3
    Amnuriak is offline Member
    Join Date
    Nov 2009
    Posts
    5
    Rep Power
    0

    Default

    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:

    Java 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 ;
       }
    }
    Then I only need to do

    Java Code:
    TCustomNodeRenderer nodeRender = new TCustomNodeRenderer();
    jTree1.setCellRenderer(nodeRender);
    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...

    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

    Java Code:
    TCustomNodeEditor nodeEditor = new TCustomNodeEditor ();
    jTree1.setCellEditor(nodeEditor );
    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.

    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.

  4. #4
    Amnuriak is offline Member
    Join Date
    Nov 2009
    Posts
    5
    Rep Power
    0

    Default

    I'm confused - yet again.

    I have two custom classes

    Java Code:
    public class TCustomNodeRenderer extends DefaultTreeCellRenderer
    and
    Java Code:
    public class TCustomNodeEditor extends DefaultTreeCellEditor
    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.

    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.

  5. #5
    zweibieren is offline Senior Member
    Join Date
    Aug 2009
    Location
    Pittsburgh, PA
    Posts
    284
    Rep Power
    5

    Default

    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 getTreeCellRendererComponent
    IS an element from the tree model.
    So the body of CutomTreeCellRenderer can reliably cast that argument to MyTreeObject.

    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);
    }
    There is a different TreeCellRenderer object for every tree cell.
    It may be helpful to imagine that getTreeCellRendererComponent
    is called every time a cell is to be displayed.

  6. #6
    Amnuriak is offline Member
    Join Date
    Nov 2009
    Posts
    5
    Rep Power
    0

    Default

    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.

  7. #7
    zweibieren is offline Senior Member
    Join Date
    Aug 2009
    Location
    Pittsburgh, PA
    Posts
    284
    Rep Power
    5

    Default

    "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.

  8. #8
    Amnuriak is offline Member
    Join Date
    Nov 2009
    Posts
    5
    Rep Power
    0

    Default

    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:

    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());
      }
    }
    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.

    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.

  9. #9
    zweibieren is offline Senior Member
    Join Date
    Aug 2009
    Location
    Pittsburgh, PA
    Posts
    284
    Rep Power
    5

    Default

    The advice above was too well hidden, so let me repeat it.
    There is a different TreeCellRenderer object for every tree cell.
    By ignoring that advice, the code in the previous post is much too elaborate.
    The equivalent "corrected" code would create a new Renderer for each cell.
    It might look like this.
    Java 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;
       }
    }
    Notes
    • 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

  1. JTree Programmatic Node Expansion and Selection Probelm
    By hemanthjava in forum AWT / Swing
    Replies: 3
    Last Post: 01-16-2013, 07:23 AM
  2. Replies: 1
    Last Post: 06-22-2009, 02:39 AM
  3. How to add JCheckbox as a node in JTree
    By shajuantony in forum javax.swing
    Replies: 0
    Last Post: 04-09-2009, 07:19 AM
  4. JSplitPane Arranging Components(JTree) Doubt
    By hemanthjava in forum AWT / Swing
    Replies: 0
    Last Post: 08-03-2008, 08:29 AM
  5. Node selection in jtree
    By Preethi in forum AWT / Swing
    Replies: 4
    Last Post: 06-19-2008, 11:25 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •