1

I am aware that there are relevant materials exists on google about JTree icons change, but still I am unable to sort out my simple requirements.

I need to change the icon of Root element of JTree different than it's node & leaf. I manage to change the node & leaf icons, but the default arrow icons (red highlighted in picture) are still there & I have to click on it to drill down the tree whereas I want to click on custom image icons to drill down the tree.

enter image description here

    /** CHANGE JTREE DEFAULT ICONS  **/
        private class userRendererForTree extends DefaultTreeCellRenderer {
            private static final long serialVersionUID = 1L;
    
            public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                    boolean leaf, int row, boolean hasFocus) {
    
                super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
                if (leaf) {
                    setIcon(setImageIcon(AppConstants.BOM_ROUTE_NODE_LEAF));
                } else {
                    setIcon(setImageIcon(AppConstants.BOM_ROUTE_NODE_RIGHT));
                }
                if (expanded) {
                    setIcon(setImageIcon(AppConstants.BOM_ROUTE_NODE_BOTTOM));
                }
                return this;
            }
        }

/**  LOAD IMAGE ICON FROM RESOURCES  **/
    private ImageIcon setImageIcon(String path){
        Image image = null;
        ImageIcon icon = null;
        try {
            image = LoadResource.getImageFromResourceAsURL(path);
            image = image.getScaledInstance(10, 10, Image.SCALE_SMOOTH);
            icon = new ImageIcon(image);
            return icon;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return icon;
    }

Calling of Renderer: -

JTree BomTree = new JTree(createTreeModel());
scrollPane.setColumnHeaderView(BomTree);
BomTree.setCellRenderer(new userRendererForTree());

UPDATED - enter image description here

camickr
  • 321,443
  • 19
  • 166
  • 288
Alpha
  • 167
  • 1
  • 13
  • 1
    Use the `UIManager` to change icons; see also [_How to remove folder symbol which comes in front of each node from JTree in java_](https://stackoverflow.com/q/5260223/230513) and [_JTree: Set custom open/closed icons for individual groups_](https://stackoverflow.com/a/14097270/230513). – trashgod Aug 10 '23 at 23:09
  • In above examples the default arrows are still there in solution as well, which I don't need that. – Alpha Aug 10 '23 at 23:25
  • Why can't you replace them with `empty` from the first example? – trashgod Aug 10 '23 at 23:30
  • check out the updated one, by applying empty it works! Now how can I make clickable closed arrow to expand & How I can set a different icon for root only? – Alpha Aug 10 '23 at 23:43
  • 1
    Note, a renderer should NOT do I/O. The Icons should be preloaded and ready to use so the renderer is efficient. – camickr Aug 11 '23 at 00:11
  • 1
    *Now how can I make clickable closed arrow to expand* - never played with JTree before but based on the first link above, it seems to me like you should use your custom Icons for the collapsed/expanded icon UIManager properties. Then use the EmptyIcon for the closed/open icon properties. – camickr Aug 11 '23 at 00:31
  • 2
    Have c loser look at the [JavaDocs for `DefaultTreeCellRenderer`](https://docs.oracle.com/en/java/javase/20/docs/api/java.desktop/javax/swing/tree/DefaultTreeCellRenderer.html), it actually lists the look and feel properties for the expand/collapse icons, but you can also configure them individually via the classes `setOpen/Close/LeafIcon` methods – MadProgrammer Aug 11 '23 at 03:09
  • @MadProgrammer well, in that case how can I make it actionable. I added `mouse listener action` but this works with whole row. I need to click only icons not the whole row? – Alpha Aug 14 '23 at 03:17
  • @Alpha The `JTree` will do it automatically - when you click on the expand/collapse handle, the tree will perform the require action itself - [for example](https://stackoverflow.com/questions/14096725/jtree-set-custom-open-closed-icons-for-individual-groups/14098574#14098574) – MadProgrammer Aug 14 '23 at 04:01

1 Answers1

1

I apologies, you mean you want to replace the expand/collapse handles, not customise the expand/closed icons of the cell ... because JTree isn't confusing

In order to do this, you either need to supply your own look and feel implementation OR customise the existing UI properties.

Thanks to TrashGod, the second method is relatively easy, just remember, this will effect ALL JTrees in your application.

enter image description here

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.put("Tree.expandedIcon", new ImageIcon(ImageIO.read(getClass().getResource("/images/icons8-expand-arrow-24.png"))));
                    UIManager.put("Tree.collapsedIcon", new ImageIcon(ImageIO.read(getClass().getResource("/images/icons8-collapse-arrow-24.png"))));

                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() throws IOException {
            setLayout(new BorderLayout());
            JTree tree = new JTree();
            tree.setShowsRootHandles(true);

            List<Item> items = new ArrayList<>(8);
            items.add(new Item("Bowle",
                    new Item[]{
                        new Item("Apple"),
                        new Item("Pear"),
                        new Item("Orangle")
                    }));
            items.add(new Item("Freezer",
                    new Item[]{
                        new Item("Top draw", new Item[]{
                            new Item("Chicken"),
                            new Item("Steak"),
                            new Item("Fish")}),
                        new Item("Bottom draw", new Item[]{
                            new Item("Pears"),
                            new Item("Chips"),
                            new Item("Ice cream")})
                    }));

            DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("My stuff");
            for (Item item : items) {
                populate(rootNode, item);
            }
            DefaultTreeModel model = new DefaultTreeModel(rootNode);
            tree.setModel(model);
            tree.setCellRenderer(new ItemTreeCellRenderer());

            add(new JScrollPane(tree));
        }

        protected void populate(DefaultMutableTreeNode parent, Item item) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(item);
            parent.add(node);

            for (Item child : item.getContents()) {
                populate(node, child);
            }
        }
    }

    class ItemTreeCellRenderer extends DefaultTreeCellRenderer {
        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
            if (value instanceof DefaultMutableTreeNode) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
                Object userValue = node.getUserObject();
                if (userValue instanceof Item) {
                    Item item = (Item) userValue;
                    value = item.getName();
                }
            }
            return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
        }
    }

    public class Item {
        private String name;
        private List<Item> contents;

        public Item(String name) {
            this.name = name;
            this.contents = new ArrayList<>(8);
        }

        public Item(String name, Item[] children) {
            this.name = name;
            this.contents = new ArrayList<>(Arrays.asList(children));
        }

        public String getName() {
            return name;
        }

        public List<Item> getContents() {
            return contents;
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366