Displaying large text in a JComboBox

Posted by {"name"=>"Palash Ray", "email"=>"paawak@gmail.com", "url"=>"https://www.linkedin.com/in/palash-ray/"} on February 03, 2014 · 7 mins read

Problem Statement

I have a list of texts, some of which are large, and I need to display them in a JComboBox. The trouble is, the JComboBox displays all the items in a single line. Consider that the following items need to be displayed:

  1. JNLP is supposed to be the latest and greatest in the Java Applet world post 1.6 release! Personally, I was never a great fan of Applets. I have always felt that Applets are a pain in the wrong place. Inherently difficult and cumbersome to use and deploy. But some of these concerns have been addressed
  2. This is really tiny
  3. As shown above, the 4 progress bars start one after another after the start button is clicked. The following code is for the JPanel with the 4 progress bars: @SuppressWarnings("serial") public class ProgressPanel extends JPanel {   private CountDownLatch startTrigger; private CountDownLatch endTrigger;   private JProgressBar bar1; private JSpinner spinner1;   private JProgressBar bar2; private

 
 
I am having the following code that displays a JComboBox in a JFrame:

package com.swayam.demo.combo;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
@SuppressWarnings("serial")
public class LargeTextComboFrameWithCustomRenderer1 extends JFrame {
    private static final List LARGE_COMBO_BOX_ITEMS = Arrays
        .asList("JNLP is supposed to be the latest and greatest in the Java Applet world post 1.6 release! Personally, I was never a great fan of Applets. I have always felt that Applets are a pain in the wrong place. Inherently difficult and cumbersome to use and deploy. But some of these concerns have been addressed",
            "This is really tiny",
            "As shown above, the 4 progress bars start one after another after the start button is clicked. The following code is for the JPanel with the 4 progress bars: @SuppressWarnings("serial") public class ProgressPanel extends JPanel {   private CountDownLatch startTrigger; private CountDownLatch endTrigger;   private JProgressBar bar1; private JSpinner spinner1;   private JProgressBar bar2; private");
    public LargeTextComboFrameWithCustomRenderer1() {
    init();
    }
    private void init() {
    getContentPane().setLayout(new BorderLayout());
    JLabel title = new JLabel("Large Text Combo Demo: Custom ComboBox Renderer 1, With Max and Preferred Size Hints", JLabel.CENTER);
    getContentPane().add(title, BorderLayout.NORTH);
    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new FlowLayout());
    JLabel comboLabel = new JLabel("Large Combo: ", JLabel.LEFT);
    mainPanel.add(comboLabel);
    mainPanel.add(getLargeComboBox());
    getContentPane().add(mainPanel, BorderLayout.CENTER);
    setSize(new Dimension(600, 500));
    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    }
    private JComboBox getLargeComboBox() {
    JComboBox largeComboBox = new JComboBox<>();
    for (String item : LARGE_COMBO_BOX_ITEMS) {
        largeComboBox.addItem(item);
    }
    largeComboBox.setRenderer(new LargeComboBoxRenderer1(200));
    largeComboBox.setPreferredSize(new Dimension(200, 30));
    largeComboBox.setMaximumSize(new Dimension(200, 30));
    return largeComboBox;
    }
    public static void main(String[] args) {
    try {
        SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
            LargeTextComboFrameWithCustomRenderer1 frame = new LargeTextComboFrameWithCustomRenderer1();
            frame.setVisible(true);
        }
        });
    } catch (InvocationTargetException | InterruptedException e) {
        e.printStackTrace();
    }
    }
}

It looks like this:
[caption id="attachment_506" align="aligncenter" width="300"]Large Text Combo Demo: Default ComboBox Renderer Large Text Combo Demo: Default ComboBox Renderer[/caption]
You can see it in action here:  
Note that the JComboBox stretches depending on the width of the largest text as rendered on a single line.

Limiting the width of the JComboBox

We can do that by setting the PrefrerredSize and the MaximumSize of the JComboBox:

    private JComboBox getLargeComboBox() {
	JComboBox largeComboBox = new JComboBox<>();
	for (String item : LARGE_COMBO_BOX_ITEMS) {
	    largeComboBox.addItem(item);
	}
	largeComboBox.setPreferredSize(new Dimension(200, 30));
	largeComboBox.setMaximumSize(new Dimension(200, 30));
	return largeComboBox;
    }

It looks like this:
[caption id="attachment_511" align="aligncenter" width="300"]Large Text Combo Demo: Default ComboBox Renderer, With Max and Preferred Size Hints Large Text Combo Demo: Default ComboBox Renderer, With Max and Preferred Size Hints[/caption]
You can see it in action here:  
The problem with this approach is that the text is truncated.

Custom Renderer which wraps large test inside HTML

We will have a custom renderer for JComboBox which wraps this text into nice HTML so that it is displayed in multi-lines.

    private JComboBox getLargeComboBox() {
	JComboBox largeComboBox = new JComboBox<>();
	for (String item : LARGE_COMBO_BOX_ITEMS) {
	    largeComboBox.addItem(item);
	}
	largeComboBox.setRenderer(new LargeComboBoxRenderer1(200));
	largeComboBox.setPreferredSize(new Dimension(200, 30));
	largeComboBox.setMaximumSize(new Dimension(200, 30));
	return largeComboBox;
    }

This is how the renderer looks like:

@SuppressWarnings("serial")
public class LargeComboBoxRenderer1 extends JLabel implements ListCellRenderer {
    private final int wordWrapWidth;
    public LargeComboBoxRenderer1(int wordWrapWidth) {
	this.wordWrapWidth = wordWrapWidth;
    }
    @Override
    public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) {
	if (isSelected) {
	    setBackground(list.getSelectionBackground());
	    setForeground(list.getSelectionForeground());
	} else {
	    setBackground(list.getBackground());
	    setForeground(list.getForeground());
	}
	setFont(list.getFont());
	setOpaque(true);
	setText(getHtmlWrappedText(value));
	return this;
    }
    private String getHtmlWrappedText(String text) {
	StringBuilder sb = new StringBuilder(300);
	sb.append("");
	sb.append("

"); sb.append(text); sb.append("

"); sb.append(""); return sb.toString(); } private String getParagraphStyle() { StringBuilder sb = new StringBuilder(100); sb.append("word-wrap: break-word;"); sb.append("width: "); sb.append(wordWrapWidth); sb.append("px;"); return sb.toString(); } }

And this is how it looks like now:
[caption id="attachment_514" align="aligncenter" width="300"]Large Text Combo Demo: Custom ComboBox Renderer 1, With Max and Preferred Size Hints Large Text Combo Demo: Custom ComboBox Renderer 1, With Max and Preferred Size Hints[/caption]
You can see it in action here:  
As shown above, this does not look good when the pop-up is not there in the JComboBox.

Finishing touches

We need to make a distinction between when there is a pop-out and when there is not. This is done by the int index parameter in the ListCellRenderer::getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) method. When index == -1, it indicates that the item is not popped out. This is the code:

    @Override
    public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) {
	if (isSelected) {
	    setBackground(list.getSelectionBackground());
	    setForeground(list.getSelectionForeground());
	} else {
	    setBackground(list.getBackground());
	    setForeground(list.getForeground());
	}
	setFont(list.getFont());
	setOpaque(true);
	// index is -1 when there is no pop-up
	if (index == -1) {
	    setText(value);
	} else {
	    setText(getHtmlWrappedText(value));
	}
	return this;
    }

This is how it looks now:
[caption id="attachment_515" align="aligncenter" width="300"]Large Text Combo Demo: Custom ComboBox Renderer 2, With Max and Preferred Size Hints Large Text Combo Demo: Custom ComboBox Renderer 2, With Max and Preferred Size Hints[/caption]
You can see it in action here:  
The sources can be found https://github.com/paawak/blog/tree/master/code/Demo.