Today's tutorial is mercifully simple. We're going to customize the look and feel of the JList component, using what's refered to as a Cell Renderer.
Recommended Experience Level (1:new - 10:expert): 3
Pre-requisites: Simple Image I/O, Swing forms
In this tutorial, we will execute the following steps:
- Create a customized data type (which we will use to populate our list).
- Create a customized Cell Renderer (by implementing
ListCellRenderer
). - Display our new list
Create a customized data type.
Our customized data type is, for our example, simply a name and an image wrapped into a data type. One of the many simple beauties about the JList
object, is that it can literally take any type of object as its list contents. This could be map tiles, beach balls, shoes, whatever. If the object contains relevant data to display to the user on a list - the JList
can handle it. We proceed with our simple construct:
import java.awt.Image;
import javax.swing.Icon;
import javax.swing.ImageIcon;
public class CustomListItem {
// The image representing this item
Image image;
// The name of this item
String name;
/**
* Set Image for this item
*
* @param image
* The image representing this item
*/
public void setImage(Image image) {
this.image = image;
}
/**
* Get the Icon for this item.
*
* @return
* An iconized form of image.
*/
public Icon getIcon() {
ImageIcon icon = new ImageIcon(image.getScaledInstance(16,16,
Image.SCALE_FAST));
return icon;
}
/**
* Set the name of this item
*
* @param name
* The new name of this item
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the name of this item
*
* @return
* The name of this item
*/
public String getName() {
return name;
}
}
With our sample class complete, we are able to move on to creation of our customized cell renderer.
Create a customized cell renderer
Before we start, let's take a look at the naming of the cell renderer. When we talk about a JList
's 'cell' we are talking about a selectable unit on the list. The JList
display list is comprised entirely of cells, each cell representing an object that belongs to the JList
.
When we create our customized cell renderer, we are literally building the routine used by the JList
to paint its cells. It will call the public Component getListCellRendererComponent()
method for each object in its list, and then place the Component object we return into the JList
's UI.
In this example, we're returning a trumped-up JLabel (JLabel is convenient because it's small and rectangular by nature, and also has a good method for managing co-existing icons and text easily) to the JList
as our display component.
/**
* A cell renderer, which takes list items (as objects) and
* translates them into list selection displays. Used
* automatically by JList
*/
static class CustomCellRenderer
implements ListCellRenderer {
@Override
/**
* When the CustomCellRenderer is loaded into our JList,
* the list will call this method automatically to get the
* Components which are used for selection on the list.
*/
public Component getListCellRendererComponent(
JList list, // The list which is calling this method
Object value, // The object which represents the value/
int index, // The index # of this object within the list
boolean isSelected, // Indicates that this item is currently
// selected.
boolean cellHasFocus // Indicates whether the cell currently
// has the focus
) {
// Cast our 'value' object into an object of our known class
CustomListItem item = (CustomListItem) value;
// Create a new JLabel (we'll return this to the list)
JLabel cell = new JLabel();
// Set the preferred size for each display 'label'
cell.setPreferredSize(new Dimension(100,20));
// Set the text portion of the cell, according to the value
// object's .toString() method.
cell.setText(item.getName());
// Set the image portion of the cell, acquiring it from the
// value object.
cell.setIcon(item.getIcon());
// Do dome styling to the label.
cell.setIconTextGap(10); // places 10 px between image & text
cell.setFont(new Font(
"Times New Roman",
Font.PLAIN,
12)); // Set ourselves a new font for the cells
// If the current cell is selected
if (isSelected) {
// Set a blue border and backdrop
cell.setBorder(new LineBorder(Color.blue));
cell.setBackground(new Color(220,220,235));
}
else
{
// Set the deault background
cell.setBorder(new LineBorder(list.getBackground()));
cell.setBackground(list.getBackground());
}
// Turn opacity on so you can see our background changes
cell.setOpaque(true);
// Return the cell to the list for display
return cell;
}
}
As you can see, it's is extremely simple to implement the ListCellRenderer
class, as it has only one method to override. Once inside of our method, all we had to do was create a new JLabel
, assign it a few properties based on the object we passed in, configure it to our liking, then ship that puppy right back out. Very, very easy to do.
Of course we aren't quite finished yet. We have to load our new customized cell renderer into the JList, and we have to put it somewhere to see it.
Display our new list
In the following code segment, we'll instantiate all that we've created, set it up, and display it. Except for a few key lines, we're looking at your basic routine frame setup.
// Create 9 custom list items
// (there are more effective ways to set up items, this is
// drawn out so you can get an idea of exactly what goes into
// making our CustomListItem objects, and how exactly the JList
// can be loaded up via constructor.
CustomListItem item1 = new CustomListItem();
CustomListItem item2 = new CustomListItem();
CustomListItem item3 = new CustomListItem();
CustomListItem item4 = new CustomListItem();
CustomListItem item5 = new CustomListItem();
CustomListItem item6 = new CustomListItem();
CustomListItem item7 = new CustomListItem();
CustomListItem item8 = new CustomListItem();
CustomListItem item9 = new CustomListItem();
// NOTE: The loadImage routine is a custom routine I drew up
// for functionality. This tutorial does not explain how to
// utilize Image I/O in java. However, you can view the complete
// code in the downloadable for this tutorial.
item1.setImage(loadImage("./image/tile/DirtFloor.png"));
item1.setName("Dirt Floor");
item2.setImage(loadImage("./image/tile/Water.png"));
item2.setName("Water");
item3.setImage(loadImage("./image/tile/Grass.png"));
item3.setName("Grass");
item4.setImage(loadImage("./image/tile/DirtFloor.png"));
item4.setName("Dirt Floor 2");
item5.setImage(loadImage("./image/tile/Water.png"));
item5.setName("Water 2");
item6.setImage(loadImage("./image/tile/Grass.png"));
item6.setName("Grass 2");
item7.setImage(loadImage("./image/tile/DirtFloor.png"));
item7.setName("Dirt Floor 3");
item8.setImage(loadImage("./image/tile/Water.png"));
item8.setName("Water 3");
item9.setImage(loadImage("./image/tile/Grass.png"));
item9.setName("Grass 3");
// Wrap all of the CustomListItem objects into an array
CustomListItem[] items = {item1, item2, item3, item4, item5,
item6, item7, item8, item9};
// Create a new list, passing the array into the constructor
JList list = new JList(items);
// Assign our custom cell renderer to the JList object
list.setCellRenderer(new CustomCellRenderer());
// Place the list inside of a scroll pane, since the JList
// does not come with a scroll bar.
JScrollPane scrollPane = new JScrollPane(list);
// Setup a frame and place our list within it.
JFrame frame = new JFrame();
frame.setLayout(new FlowLayout());
frame.add(scrollPane);
frame.setSize(600,400);
frame.setTitle("Custom Style List Tutorial");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
After creating our custom objects, and processing them through our JList (and, subsequently, through our customized cell renderer), we are able to affect the look and feel of our list any way we like.
Happy Coding!
Timon
Download the tutorial files (note: you will probably have to do some editing and/or file movement to use the default images included in the download).
As always, feel free to peruse the Tutorial repository at http://code.google.com/p/tutorialsbytimon"