banner

JLists and JComboBox

Optional Reading

We will be using the Creating a GUI with JFC/Swing tutorial from Sun as a Reference source. You are not required to read the entire tutorial, I suggest you skim through it though to use a a supplement to what I give you here.

JLists

A JList(The API has some great examples by the way) is a component that can be quite simple, or you can really let go and make it powerful, but more complex. JLists will let you:

In this section, I'll show some examples of several of the options. If there is one thing you should take away from a JList is that it just doesn't hold a list of Strings! It can, but the real power and beauty of the JList is that it can hold any type of Object, and you can customize the renderer to show that object in any way within your list.

Making a static list of strings is easy. JList has four constructors:

We can use the third constructor above to have JList automatically build an internal data model that will hold a list of Strings. Don't forget to put the JList in JScrollPane so that it will scroll! By default, a JList's preferred size is 8 items, but you can change that if you wish.

String[] strings={"One", "Two", "Three", "Four"};
JList list = new JList(strings);
JScrollPane jsp = new JScrollPane(list);
JFrame jf = new JFrame();
jf.add(jsp, BorderLayout.CENTER);
...

Simple JList

By default, a JList lets you select as many items simultaneously as you want. There are methods to turn off this behavior and only let you select one item at a time.

Ok, you probably want somebody to be able to select an item in the JList. First, you can always call getSelectedValue() or getSelectedValues() to see what is selected in the list. If you push a button to get the selected values, you can then get the values and do with them what you will.

What if you wanted to make something happen as the user clicked on the item in the list, and not make them click on another button? In this case you need to listen to List Selection Events. To receive notification of a selection, you need to register a ListSelectionListener using the addListSelctionListener() method of JList.

ListSelectionListener interface has only one method

public abstract void valueChanged(ListSelectionEvent evt)

The ListSelectionEvent contains source (JList) of the event, two indices that indicate a range of values between which a change has occurred and a flag which indicates the selection is still changing. You may get intermediate ListSelectionEvents before the user is done, but when they are done, the flag indicating the value is adjusting will be FALSE.

Download and open the Netbeans project simplelist.

When you first run the app, you'll see the following window:

JList

If you select one of the items:

JList selected

You'll see the following in your output window

List Selection Output
List selection output

Modifying a JList

Ok, now what if you want to change the contents of a JList on the fly? Your best bet it to use a DefaultListModel which implements the ListModel interface. You can pass this object to the constructor, and then as you update the model, the contents of the JList will change.

If you created a JList with an array of Objects or a Vector, even if you update the data container, the JList won't update. You actually have to "reset" the data to the JList by using the setListData() method and pass it the newly modified data container.

Download and open the Netbeans project dynamiclist. This example has a little more going on internally. When you first open the app, you get the following window:

Empty List

Try typing things into the JTextField between the two buttons. Click on the "Add" button to add it to the list.

Populated List

If you want to remove an object, you can click on the item in the list and it's name appears in the JTextField, if you then click on Remove, it will be deleted from the list.

Custom Data Model

This next example may seem trivial, but it underscores how flexible the data model can be. Download and open the Netbeans project dynamiclistmodel.

Run the example, you should see something like this:

Custom List Model

Before you do anything else, take a look in you output window. Notice the CustomListModel.getElementAt() calls that are printed out.

When a JList is shown, it normally walks through every element it has (using getElement()) and calculating which is the widest element so it knows how wide it would like to be. If you look at the code, we use setPrototypeCellValue() which tells JList to just use the provided item to size the cells. With this in place, you can see that JList only needs to get info on the items displayed, not the whole list.

Comment out the setPrototypeCellValue() call and run the app again. What do you see?

The point of this exercise is that you don't need an actual data structure to populate a list, all you need is something that implements the ListModel interface, and then the only thing that matters is the getElement() call. It could even be a call to a database.

Custom Renderer

As the final example, I'm going to get a little more complex with the data. Let's say I want to create a list showing menu items that can be ordered from a take-out store.

I've made a Food object, which has four attributes

Nothing fancy, all the attributes but the icon are Strings, and the icon is an object that implements the Icon interface.

My "main" object is a JFrame, but it has a method addFood(Food) that adds the food items to the DefaultListModel that the JList uses. If I made some food objects and added them to the list model like this:

main.addFood(new Food("Coca Cola", "1.00", "12 oz Can of Coke", new ImageIcon(main.getClass().getResource("soda.gif"))));

main.addFood(new Food("Sprite", "1.00", "12 oz Can of Sprint", new ImageIcon(main.getClass().getResource("soda.gif"))));

main.addFood(new Food("Blueberry Muffin", "1.50", "Blueberry Muffin", new ImageIcon(main.getClass().getResource("muffin.gif"))));

I would have a list of Food items in my JList. What would it look like if I ran it as is? Turns out the default renderer for a list is to render Icons as icons, otherwise it calls the toString() on the object being rendered. If we didn't define a toString() method, we get the default which would look like this:

Renderer example

Not what we were hoping for. You could get some interesting behavior by just overriding the toString() method, but it would still just be text.

Renderer example

But if we defined our own custom renderer, we could generate anything we wanted in the list. Take a look at the code below:

public class FoodRenderer extends DefaultListCellRenderer {

public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
JLabel renderer = (JLabel)(super.getListCellRendererComponent(list, value, index, isSelected, hasFocus));
if (value instanceof Food) {
Food food = (Food)value;
renderer.setText(food.getName() + " ($" + food.getCost()+ ")");
renderer.setIcon(food.getIcon());
renderer.setToolTipText(food.getDescription());
}
return renderer;
}

The class DefaultListCellRenderer implements the ListCellRenderer interface (surprised? I bet you were). This code is a common design pattern when implementing custom renderers. We override the getListCellRenderer() method and call the super class method to get a renderer that is already marked up for the object. This is the default renderer, but it has background colors, etc... that will be still correct. Next we have to make sure that value (the object being rendered) is of type Food, if it is, we can cast it to Food, and then update the renderer text and icon from the Food object.

Also note that we set something called ToolTipText in this renderer. Watch what happens when your mouse "hover" over an entry in the list.

DefaultListCellRenderer inherits from JLabel, so that is why we can cast it too that.

Download and install the Netbeans project jlistrenderer. When you run the app, the following window appears:

JList w/ custom renderer

We no longer have a plain list. Each item has an Icon, and notice that the cost of the item is also displayed. If you let your mouse hover over an item (in this case, it hovered over Apple), the tooltiptext pops up with the "description" of the item.

JList tooltip

JList can easily display a simple list, but with only a little work, you can have a very nice JList showing far more than just text. Think about the example above, when you click on an item, and call getSelectedItem(), do you get a String that you have to look up? No, you get the Food object itself.

JComboBox

JComboBox another class that shows lists. Unlike a JList, the JComboBox allows you to enter your own data. Think of a JComboBox a a JTextField with a button that pops up a JList. When you pop up the JList, you can choose an item to populate the original JTextField.

Since JComboBox uses a JList like window, you can even use your a ListCellRenderer to show information.

Unlike a JList, you don't have easy access to the data model, and you just call addItem() on the combo box. If we make a quick modification to the last JList example, we can use a JComboBox instead.

Download and open the Netbeans project jcombobox. When you run it you'll get an initial window like this:

JComboBox

And if you click on the inverted triangle at the right of the window, you'll get the popup list

JCombBox

I briefly mentioned that you can make a JComboBox editable. Well, this is sort of true. If we call setEditable(true) on the above example:

Editable JComboBox

We now have an editable text field. Notice that it is calling the toString() method on the selected object. If you tried to change the text, it would let you, but you'd no longer have a "Food" object in the list, instead you'd just have a String.

If you clicked on the triangle, you'd still get the original list:

Editable JComboBox

So remember, editable JCombBox only work with Strings!

f you clicked on the triangle, you'd still get the original list:

Editable JComboBox

So remember, editable JCombBox only work with Strings!