Customizing listbox/combobox items

Last week I wrote about using a variety of techniques to customize listbox items with an attached checkbox icon. Some of these methods used a standard Matlab listbox uicontrol, others used other controls. Today I wish to extend the discussion and show how Matlab listbox and combobox (pop-up) items can be customized in a variety of ways.

To add icons to listbox/combobox items, we could use standard HTML, as I’ve shown last week. This is the simplest method, requires no Java knowledge, and it usually works well. The problem is that when a listbox/combobox has numerous items (hundreds or more), it may start getting sluggish. In such case it is faster to use a dedicated Java cell-renderer that sets the icon, font, colors, tooltip and other aspects on an item-by-item basis. This runs faster and enables far greater customizability than what is possible with HTML. The drawback is that it requires some Java programming. No free lunch…

Listbox and combobox cell-renderers need to extend javax.swing.ListCellRenderer, similarly to uitable cell-renderers. This is basically a simple Java class that minimally contains just an empty constructor and a getListCellRendererComponent() method with a predefined signature. getListCellRendererComponent() is automatically called by the Swing render engine separately for each listbox item, and gets as input args a JList reference, the item value (typically a string), an integer list index, a boolean flag indicating whether the item is currently selected, and another flag indicating whether the item is currently in focus. getListCellRendererComponent() uses these parameters to customize and return a java.awt.Component, which is typically (but not necessarily) a standard Swing JLabel.

Here is a simple example that displays a folder of icon files in a Matlab listbox and combobox. Each item is the filename, with a customization that if the file is an icon, then this icon is displayed next to the file name, otherwise the name appears in red italic without an icon. For illustration, we’ll use Matlab’s builtin icons folder: %matlabroot%/toolbox/matlab/icons/:

Custom listbox cell-renderer    Custom combobox cell-renderer

Creating the cell renderer

We start by creating a custom ListCellRenderer. Place the following code in a file called LabelListBoxRenderer.java:

import java.awt.*;
import javax.swing.*;
import java.util.Hashtable;
 
public class LabelListBoxRenderer extends JLabel implements ListCellRenderer
{
    private String folderPath;
    private final Hashtable<String,ImageIcon> iconsCache = new Hashtable<String,ImageIcon>();
 
    // Class constructors
    public LabelListBoxRenderer() {
        setOpaque(true);
        setHorizontalAlignment(LEFT);
        setVerticalAlignment(CENTER);
    }
    public LabelListBoxRenderer(String folderPath) {
        this();
        this.folderPath = folderPath;
    }
 
    // Return a label displaying both text and image.
    public Component getListCellRendererComponent(
            JList list,
            Object value,
            int index,
            boolean isSelected,
            boolean cellHasFocus)
    {
        String label = value.toString();
        setFont(list.getFont());
        if (isSelected) {
            // Selected cell item
            setBackground(list.getSelectionBackground());
            setForeground(list.getSelectionForeground());
        } else {
            // Unselected cell item
            setBackground(list.getBackground());
            setForeground(list.getForeground());
        }
        try {
            String iconFname = (folderPath + "/" + label).replace('\\', '/');
            ImageIcon icon = getFileIcon(iconFname);
            setIcon(icon);
            if (icon.getIconWidth() > 0) {
                // Cell item is a valid icon filename
                list.setToolTipText(iconFname + " (" + icon.getIconWidth() + " x " + icon.getIconHeight() + ")");
            } else {
                // Cell item is not an icon filename
                list.setToolTipText(iconFname + " (not an icon)");
                setFont(getFont().deriveFont(Font.ITALIC));
                setForeground(Color.red);
            }
        } catch (Exception e) {
            list.setToolTipText(e.getMessage());
        }
        //System.out.println(index + ": " + label);  // debug console printout
        setText(label);
        return this;
    }
 
    // Modify the folder path (default = current folder)
    public void setFolderPath(String folderPath) {
        this.folderPath = folderPath;
    }
 
    // Lazily load the file icons only as needed, later reuse cached data
    private ImageIcon getFileIcon(String filename) {
        ImageIcon icon;
        if (iconsCache.containsKey(filename)) {
            // Reuse cached data
            icon = iconsCache.get(filename);
        } else {
            // Lazily load the file icons only as needed
            icon = new ImageIcon(filename);
            iconsCache.put(filename, icon);  // store in cache for later use
        }
        return icon;
    }
}

In the code above, I’ve cached the ImageIcons, so that the actual disk file is only accessed once rather than repeatedly whenever the cell needs to be rendered. For even improved performance, we could also cache the tooltips and derived italic font so that they would not be regenerated each time (note that the list’s font is not the same as the cell component’s font) – I will leave this as an exercise to the reader.

Next, compile this file (using the standard javac compiler or any Java IDE), ensuring that you target a JVM compatible with your Matlab (JVM 5 will work on R14SP2 onward, 6 on R2007b onward, and 7 only on R2013b or newer). For convenience, both the source and compiled files for the LabelListBoxRenderer class can be downloaded here: LabelListBoxRenderer.java, LabelListBoxRenderer.class.

Using the cell-renderer in Matlab listboxes

Now that we have a custom cell renderer, we should add the LabelListBoxRenderer.class file to Matlab’s Java classpath using the javaaddpath function and then use this class in Matlab:

% Create the Matlab listbox
iconsFolder = fullfile(matlabroot, 'toolbox/matlab/icons');
files = dir(iconsFolder);
hListbox = uicontrol('Style','list', 'String',{files.name}, 'Position',[10,10,120,150]);
 
% Find the uicontrol's underlying Java component
jScrollpane = findjobj(hListbox);
jListbox = jScrollpane.getViewport.getView;
 
% Update the listbox's cell-renderer
javaaddpath 'C:\Yair\Undocumented Matlab\Work\'   % location of my LabelListBoxRenderer.class
jRenderer = LabelListBoxRenderer(iconsFolder);
jListbox.setCellRenderer(jRenderer);
 
% Give the icons some space...
jListbox.setFixedCellHeight(18);

Custom listbox cell-renderer

Wasn’t too painful was it?

Using the cell-renderer in Matlab combo-boxes

Customizing Matlab combo-boxes is just as easy, and uses the same LabelListBoxRenderer class:

% Create the Matlab combobox
iconsFolder = fullfile(matlabroot, 'toolbox/matlab/icons');
files = dir(iconsFolder);
hCombobox = uicontrol('Style','popup', 'String',{files.name}, 'Position',[10,10,120,150]);
 
% Find the uicontrol's underlying Java component
jCombobox = findjobj(hCombobox);  % no scroll-pane for combos
 
% Update the combobox's cell-renderer
javaaddpath 'C:\Yair\Undocumented Matlab\Work\'   % location of my LabelListBoxRenderer.class
jRenderer = LabelListBoxRenderer(iconsFolder);
jCombobox.setRenderer(jRenderer);  % Note: not setCellRenderer()
 
% Give the icons some space...
jCombobox.setFixedCellHeight(18);
 
% Make the drop-down list shorter than the default (=20 items)
jCombobox.setMaximumRowCount(8);

Custom combobox cell-renderer

For additional aspects of listbox and combobox customizations, refer to sections 6.6 and 6.7 of my Matlab-Java programming book.

Categories: GUI, Java, Medium risk of breaking in future versions, UI controls

Tags: , , ,

Bookmark and SharePrint Print

One Response to Customizing listbox/combobox items

  1. Pingback: Customizing combobox popups | Undocumented Matlab

Leave a Reply


Your email address will not be published. Required fields are marked *