How do I create JTree with different icons for each node?

The example below demonstrates you how to create a JTree that have a different icons for each node of the tree.

package org.kodejava.swing;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
import java.net.URL;

public class JTreeDifferentNodeIcon extends JFrame {
    public JTreeDifferentNodeIcon() throws HeadlessException {
        initializeUI();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
                () -> new JTreeDifferentNodeIcon().setVisible(true));
    }

    private void initializeUI() {
        setSize(200, 400);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Countries");
        DefaultMutableTreeNode asia = new DefaultMutableTreeNode("Asia");
        Country[] countries = new Country[]{
                new Country("India", "/flags/in.png"),
                new Country("Singapore", "/flags/sg.png"),
                new Country("Indonesia", "/flags/id.png"),
                new Country("Vietnam", "/flags/vn.png"),
        };

        for (Country country : countries) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(country);
            asia.add(node);
        }

        DefaultMutableTreeNode northAmerica = new DefaultMutableTreeNode("North America");
        countries = new Country[]{
                new Country("United States", "/flags/us.png"),
                new Country("Canada", "/flags/ca.png")
        };

        for (Country country : countries) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(country);
            northAmerica.add(node);
        }

        DefaultMutableTreeNode southAmerica = new DefaultMutableTreeNode("South America");
        countries = new Country[]{
                new Country("Brazil", "/flags/br.png"),
                new Country("Argentina", "/flags/ar.png"),
                new Country("Uruguay", "/flags/uy.png")
        };
        for (Country country : countries) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(country);
            southAmerica.add(node);
        }

        DefaultMutableTreeNode europe = new DefaultMutableTreeNode("Europe");
        countries = new Country[]{
                new Country("United Kingdom", "/flags/gb.png"),
                new Country("Germany", "/flags/de.png"),
                new Country("Spain", "/flags/es.png"),
                new Country("France", "/flags/fr.png"),
                new Country("Italy", "/flags/it.png")
        };
        for (Country country : countries) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(country);
            europe.add(node);
        }

        root.add(asia);
        root.add(northAmerica);
        root.add(southAmerica);
        root.add(europe);

        final JTree tree = new JTree(root);
        tree.setCellRenderer(new CountryTreeCellRenderer());
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.addTreeSelectionListener(e -> {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
            if (node == null) {
                return;
            }

            Object nodeInfo = node.getUserObject();
            if (node.isLeaf()) {
                Country country = (Country) nodeInfo;
                System.out.println("country = " + country.getName());
            }
        });

        JScrollPane pane = new JScrollPane(tree);
        pane.setPreferredSize(new Dimension(200, 400));

        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(pane, BorderLayout.CENTER);
    }

    static class CountryTreeCellRenderer implements TreeCellRenderer {
        private final JLabel label;

        CountryTreeCellRenderer() {
            label = new JLabel();
        }

        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
                                                      boolean leaf, int row, boolean hasFocus) {
            Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
            if (userObject instanceof Country country) {
                URL imageUrl = getClass().getResource(country.getFlagIcon());
                if (imageUrl != null) {
                    label.setIcon(new ImageIcon(imageUrl));
                }
                label.setText(country.getName());
            } else {
                label.setIcon(null);
                label.setText(value.toString());
            }
            return label;
        }
    }

    static class Country {
        private String name;
        private String flagIcon;

        Country(String name, String flagIcon) {
            this.name = name;
            this.flagIcon = flagIcon;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getFlagIcon() {
            return flagIcon;
        }

        public void setFlagIcon(String flagIcon) {
            this.flagIcon = flagIcon;
        }
    }
}

Here is the result of our program above:

JTree Nodes With Icons

JTree Nodes With Icons

Flag icons by FamFamFam Flag Icons.

Wayan

10 Comments

  1. Hi Hades,

    If the imageUrl is null it means that it cannot find the flags icon. You need to place the icon under the resources directory.

    ├─ pom.xml
    └─ src
       └─ main
          ├─ java
          │   └─ org
          │      └─ kodejava
          │         └─ swing
          │            └─ JTreeDifferentNodeIcon.java
          └─ resources
             └─ flags
                ├─ ar.png
                ├── br.png
    
    Reply
    • Hi Doug,

      You can add the following snippet if you want to make the JTree node selectable.

      tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
      tree.addTreeSelectionListener(new TreeSelectionListener() {
          @Override
          public void valueChanged(TreeSelectionEvent e) {
              DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
              if (node == null) {
                  return;
              }
      
              Object nodeInfo = node.getUserObject();
              if (node.isLeaf()) {
                  Country country = (Country) nodeInfo;
                  System.out.println("country = " + country.getName());
              }
          }
      });
      
      Reply
  2. Hi, thanks for the tutorial, mine working just fine. The only problem is that the selectable listener not functioning. Nothing happened when selected the tree element. Console also not print the sys out. Might have something to do with my IDEA. Because I run it with NetBeans 12 Rosetta mode on Apple silicon machine.

    Reply

Leave a Reply to icefrogCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.