tags:

views:

1011

answers:

2

I am using a CustomCellRenderer to display nodes of a JTree to display image with a node as shown below :-

class CustomTreeCellRenderer extends DefaultTreeCellRenderer{

    public Component getTreeCellRendererComponent(JTree tree,
        Object value, boolean selected, boolean expanded,
        boolean leaf, int row, boolean hasFocus){

        super.getTreeCellRendererComponent(tree, value,
        selected, expanded, leaf, row, hasFocus);

        JLabel label = (JLabel) this ;

        label.setIcon( new ImageIcon("white.png") ) ;

        return this;
    }
}

My requirement is to change image of the node on some external action. I am trying to reload the model of JTree, but it's not working as shown below :-

public void actionPerformed(ActionEvent ae) {

        DefaultTreeModel model = (DefaultTreeModel) tree.getModel() ;

        JLabel label = (JLabel) tree.getCellRenderer() ;
        System.out.println(label.getIcon()); //displaying white.png

        label.setIcon( new ImageIcon("black.gif") ) ;


        model.reload() ;
    }

Where I am doing wrong??????

+1  A: 

Add your Icon to your renderer class as a field.

Change the value of this field and repaint the JTree.

class CustomTreeCellRenderer extends DefaultTreeCellRenderer{


    ImageIcon rendererIcon;


    public void setRendererIcon(ImageIcon myIcon){
         this.rendererIcon = myIcon;
    };


    public Component getTreeCellRendererComponent(JTree tree,
        Object value, boolean selected, boolean expanded,
        boolean leaf, int row, boolean hasFocus){

        super.getTreeCellRendererComponent(tree, value,
        selected, expanded, leaf, row, hasFocus);

        JLabel label = (JLabel) this ;

        label.setIcon( rendererIcon ) ;

        return this;
    }
}

Edit: explanations

In your case, it is useless to change the model. The icon used to display each of the nodes is a part of the renderer.

The renderer object of the JTree is not forced to be a JComponent. It has to be an object providing a JComponent when the code calls getTreeCellRendererComponent.

In your case, the cast of getCellRenderer() into a JLabel is just plain luck, because the default implementation of the DefaultTreeCellRenderer is an extension of JLabel.

And, in fact, as your renderer did call setIcon on itself, it's normal that your getIcon methods gives you the last icon you did put into the renderer.

Full code working :

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultTreeCellRenderer;

public class TestJTree {

    private static ImageIcon iconWhite = new ImageIcon("white.jpg");

    private static ImageIcon iconBlack = new ImageIcon("black.jpg");;

    public static void main(String[] args) {
        TestJTree me = new TestJTree();
        me.process();
    }

    private void process() {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                initGui();

            }

        });

    }

    protected void initGui() {
        JFrame frame = new JFrame("Test JTree");
        frame.setContentPane(new JPanel(new BorderLayout()));

        final JTree tree = new JTree();
        frame.getContentPane().add(tree);

        final CustomTreeCellRenderer renderer = new CustomTreeCellRenderer();
        renderer.setRendererIcon(iconWhite);
        tree.setCellRenderer(renderer);

        JPanel panelButtons = new JPanel();

        JButton buttonWhite = new JButton("");
        buttonWhite.setIcon(iconWhite);
        JButton buttonBlack = new JButton("");
        buttonBlack.setIcon(iconBlack);

        buttonBlack.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e) {
                renderer.setRendererIcon(iconBlack);
                tree.repaint();
            }

        });

        panelButtons.add(buttonBlack);
        panelButtons.add(buttonWhite);
        frame.getContentPane().add(panelButtons,BorderLayout.SOUTH);

        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500,500);
        frame.setVisible(true);


    }

    @SuppressWarnings("serial")
    private static class CustomTreeCellRenderer extends DefaultTreeCellRenderer{


        ImageIcon rendererIcon;


        public void setRendererIcon(ImageIcon myIcon){
             this.rendererIcon = myIcon;
        };


        public Component getTreeCellRendererComponent(JTree tree,
            Object value, boolean selected, boolean expanded,
            boolean leaf, int row, boolean hasFocus){

            Component ret = super.getTreeCellRendererComponent(tree, value,
            selected, expanded, leaf, row, hasFocus);

            JLabel label = (JLabel) ret ;

            label.setIcon( rendererIcon ) ;

            return ret;
        }
    }
}
Laurent K
I have tried that, but it's not working.
rits
@ritsThe code that I added is working for me
Laurent K
@Laurent Yes, it's changing icon for the whole Tree, not for the selected node.
rits
+1  A: 

a couple points:

  1. grabbing the renderer from the table in a method like actionPerformed and modifying it is not common practice. You should note that the renderer is shared, so you will be affecting all cells in the column that use that renderer
  2. even though you set the Icon on your renderer instance during actionPerformed, the renderer is always accessed for painting via the getTreeCellRendererComponent method, and in that you are always setting the icon to "white.png", so you will never get the "black.gif" to display.

An option you have is to set state on the Model in your actionPerformed method, and then from within the getTreeCellRendererComponent you can query your model for the icon to be displayed.

for example:

public void actionPerformed(ActionEvent ae) {

    MyCustomTreeModel model = (MyCustomTreeModel) tree.getModel() ;
    ...
    model.setMyState(state); //set the state based on the action
}

in the renderer:

public Component getTreeCellRendererComponent(JTree tree,
    Object value, boolean selected, boolean expanded,
    boolean leaf, int row, boolean hasFocus)
{
     MyCustomTreeModel model = (MyCustomTreeModel) tree.getModel();
     ....
     setIcon(model.getMyIconBasedOnTheStateISetInActionPerformed());
     return this;    
}
akf
then how to solve this issue. explanation with code is appreciated. :)
rits