Question

I have a class GridPanel extends JPanel, with a static inner class ToolSelectComboBox extends JComboBox, which in turn has two static inner classes ToolSelectComboBoxModel implements ComboBoxModel and ToolSelectComboBoxRenderer implements ListCellRenderer. The panel displays the ToolSelectComboBox (TSCB), whose constructor sets its model and the renderer to be the custom ones I created. The box is created correctly, and its models and renderers work correctly.

However, the Renderer's getListCellRendererComponent(...) method uses an ImageIcon on the JLabel that it returns. The icon is loaded correctly, but, the first time I click the combo box (on each run), the image takes exactly (or at least very very close to) a little more than a second to load. I would assume that this is some lag in loading the file, except that

  • It is a 4kB file on my local file system
  • When I add System.out.println commands before and after the result.setIcon(...) command, they follow each other almost instantly.

The strange thing that I notice is that the println commands are fired twice, once when I click the box, and again when the icon loads.

It may also be worth noting that, as this designed to work with multiple classes that override a single method of the parent abstract class (to generate the path to the icon), when I noticed that this was working slowly, I changed the code from simply retrieving the icon with the getIcon command to storing the various sized icons (16, 32, and 64 px squared) in a TreeMap<Tool.ImageSize, ImageIcon> (where Tool is an interface I created that has a ImageIcon getIcon() method.

All my imports are in order.

Any help would be appreciated!

I apologize if I have posted too much code, but I wanted to make sure that it's understandable. On the other hand, if more code would be required for you to understand, please don't hesitate to ask.

Code (all lines that begin with "*" and have comment-like text are collapsed JavaDoc tags, not just messed-up code):

public class GridPanel extends JPanel {

    public static class ToolSelectComboBox extends JComboBox {

         // Combo box model `ToolSelectComboBoxModel` snipped

         * A renderer for the {@link ToolSelectComboBoxModel}. This may
        public static class ToolSelectComboBoxRenderer implements
                ListCellRenderer {

             * The default renderer. Only the icon and text are modified.
            protected DefaultListCellRenderer d = new DefaultListCellRenderer();

            @Override
            public Component getListCellRendererComponent(final JList list,
                    final Object value, final int index,
                    final boolean isSelected, final boolean cellHasFocus) {
                if (!ToolSelectComboBoxModel.class.isInstance(list.getModel())) {
                    throw new IllegalStateException(
                            "Cannot use a ToolSelectComboBoxRenderer on any list model type other than ToolSelectComboBoxModel.");
                }
                final JLabel result = (JLabel) d.getListCellRendererComponent(
                        list, value, index, isSelected, cellHasFocus);
                result.setText(null);
                if (value != null) {
                    result.setIcon(((Tool) value)
                            .getIcon(Tool.IconSize.SIZE_32PX));
                }
                return result;
            }
        }

        public ToolSelectComboBox() {
            setModel(new ToolSelectComboBoxModel());
            ((ToolSelectComboBoxModel) getModel()).add(new CircleTool()); // shown below
            setRenderer(new ToolSelectComboBoxRenderer());
        }
    }

     * Create the panel.
    public GridPanel() {
        setLayout(new BorderLayout(0, 0));

        final ToolSelectComboBox toolSelectComboBox = new ToolSelectComboBox();
        add(toolSelectComboBox, BorderLayout.NORTH);

        final SquareGrid squareGrid = new SquareGrid(); // another class; this works fine
        add(squareGrid, BorderLayout.CENTER); // irrelevant to problem

    }

}

The CircleTool class only has one method (to override AbstractTool's abstract method to get the image path) and, since the method works (it gets the path fine, it's just the icon that loads slowly), I have not included this class.

The AbstractTool class:

public abstract class AbstractTool implements Tool {

    /**
     * A {@link TreeMap} to map the icon sizes to their icons.
     */
    protected final TreeMap<Tool.IconSize, ImageIcon> map = new TreeMap<Tool.IconSize, ImageIcon>();

    /**
     * Constructs the tool and sets up the {@linkplain #map}.
     */
    public AbstractTool() {
        for (final Tool.IconSize size : Tool.IconSize.values()) {
            System.out.println("Putting value for " + size);
            map.put(size,
                    new ImageIcon(Tool.class.getResource(getImagePath(size))));
        }
    }

    @Override
    public ImageIcon getIcon(final IconSize size) {
        return map.get(size);
    }

    /**
     * Gets the image path for the given image size.
     * 
     * @param size
     *            the size
     * @return the image path
     */
    protected abstract String getImagePath(Tool.IconSize size);

}
Was it helpful?

Solution

but, the first time I click the combo box (on each run), the image takes a little more than a second to load. I would assume that this is some lag in loading the file

That would be my guess as well.

except that when I add System.out.println commands before and after the result.setIcon(...) command, they follow each other almost instantly

When you click on the combo box all the code runs on the EDT which means each Icon would be loaded sequentially.

However System.out.println() runs on a separate Thread so it is displayed immediately.

The solution is to load the icons at program start up. That is whenever you define/add the Icons to your map you should read them in at that time. You may want to do that on a separate Thread so you don't block the GUI from displaying.

Edit:

Here is a simple SSCCE that displays icons in a combo box:

import java.awt.*;
import javax.swing.*;

public class ComboBoxIcon extends JFrame
{
    JComboBox comboBox;

    public ComboBoxIcon()
    {
        Object[] items =
        {
            new ImageIcon("about16.gif"),
            new ImageIcon("add16.gif"),
            new ImageIcon("copy16.gif")
        };
        comboBox = new JComboBox( items );
        getContentPane().add( comboBox, BorderLayout.NORTH );
    }

    public static void main(String[] args)
    {
        JFrame frame = new ComboBoxIcon();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}

If you need more help then you need to post your SSCCE that demonstrates the problem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top