I have a JList created with a DefaultListModel and a custom cell renderer.
After several tries I have got this to work up until I update the list
model. As soon as I do, no matter how I do it, the entire JList goes blank,
including the enclosing JScrollPane, despite the fact that my custom cell
renderer is invoked to paint the cells.
Note that this is in JDK 1.4.2.
The cell renderer looks like:
public class LayoutColumnRenderer extends Component
implements ListCellRenderer
{
public LayoutColumnRenderer(JList list)
{
...
} // LayoutColumnRenderer.LayoutColumnRenderer
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean hasFocus) {
...
return this;
} // LayoutColumnRenderer.getListCellRendererComponent
public void paint(Graphics g) {
g.setColor(getBackground());
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(getForeground());
g.drawString(text, 0, fm.getAscent());
} // LayoutColumnRenderer.paint
} // class LayoutColumnRenderer
The JList is created as follows:
private Vector selectedFlds;
private JList selectedList;
private DefaultListModel selectedListModel;
...
selectedListModel = new DefaultListModel();
selectedListModel.ensureCapacity(selectedFlds.size());
Iterator iter = selectedFlds.iterator();
while(iter.hasNext())
{
selectedListModel.addElement(iter.next());
}
selectedList = new JList(selectedListModel);
selectedList.setVisibleRowCount(10);// show 10 rows at a time
selectedList.clearSelection(); // nothing selected for action
selectedList.setCellRenderer(new LayoutColumnRenderer(selectedList) );
selectedSp = new JScrollPane(selectedList);
...
The list is updated as follows. LayoutColumn is the class of elements that
appear in the selectedFlds Vector.
LayoutColumn lc;
lc = new LayoutColumn(field, this, log);
try {
selectedListModel.add(after, lc);
}
catch (ArrayIndexOutOfBoundsException obe)
{
...
}
selectedFlds.insertElementAt(lc, after++);
...
As a result of calling DefaultListModel.insertElementAt
LayoutColumnRenderer.paint is called ONCE FOR EVERY ELEMENT IN THE UPDATED
ListModel. That is it is not called only for those elements of the list
that are currently visible through the window of the scroll pane, as it was
when the JList was first displayed, and as it is each time I scroll through
the JList. It is called even for those elements that are not currently
visible. And yet, despite that excessive processing, none of the cells are
repainted on the screen, and even the scroll bar elements disappear. Based
upon the calls to my paint method I can see that the ListModel was correctly
updated, it just doesn't display properly.
This does not happen if LayoutColumnRenderer is derived from JLabel, so I
suspect that I am hitting some obscure interaction with JScrollPane that
JLabel knows all about, but which is obscured by the coding style of Swing
components. I cannot use JLabel because the output painting is actually a
bit more complicated than what I show above, so I cannot leave the default
paint function of JLabel.
Any ideas? There may be a bug in JList that it calls my paint method too
many times after the ListModel is updated, but aside from that why does
nothing appear when my paint method is called?

Signature
Jim Cobban jcobban@magma.ca
34 Palomino Dr.
Kanata, ON, CANADA
K2M 1M1
+1-613-592-9438
Jim Cobban - 02 Nov 2004 23:41 GMT
I put a lot of debugging code in and I understand part of what is going on,
but not how to fix it.
After updating the ListModel, as expected, my getListCellRendererComponent
method is called. I return with components that have exactly the same
dimensions as the first time, of course. The largest of these components is
200 pixels wide by 16 pixels high. When I created the JList in the first
place this resulted, as expected, in my paint method being called, for each
of the cells that was visible in the scroll pane, with a width of 200 and a
height of 16. However after adding an element to the ListModel, even though
my getListCellRendererComponent continues to respond with Components that
are 16 pixels high, JList somehow comes to the conclusion that they are only
1 pixel high. This has two consequences:
1) The JScrollPane decoration disappears because since the components are
only 1 pixel high, naturally all of them fit within the window.
2) The contents of the cells are all blank because when I write the 16 point
high text onto the cells only the very top pixels are going to be drawn
because the graphics context understands that the Component is only 1 pixel
high!
So why, when my getListCellRendererComponent method returns a Component with
a height of 16 pixels (verified by calling getHeight() immediately before
the return from this method), why does JList somehow come to the conclusion
that the Components are only 1 pixel high after the ListModel is updated?
> I have a JList created with a DefaultListModel and a custom cell renderer.
> After several tries I have got this to work up until I update the list
[quoted text clipped - 88 lines]
> many times after the ListModel is updated, but aside from that why does
> nothing appear when my paint method is called?
Andrei Kouznetsov - 03 Nov 2004 11:00 GMT
> After updating the ListModel, as expected, my getListCellRendererComponent
> method is called. I return with components that have exactly the same
[quoted text clipped - 6 lines]
> are 16 pixels high, JList somehow comes to the conclusion that they are only
> 1 pixel high.
your custom renderer should return appropriate preferredSize (@see
get/setPreferredSize())

Signature
Andrei Kouznetsov
http://uio.dev.java.net Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities
Jim Cobban - 03 Nov 2004 16:30 GMT
> > After updating the ListModel, as expected, my getListCellRendererComponent
> > method is called. I return with components that have exactly the same
[quoted text clipped - 14 lines]
> your custom renderer should return appropriate preferredSize (@see
> get/setPreferredSize())
Thank you Andrei. That does correct the problem, but I do not understand
why.
Remember that I am deriving from Component, not JComponent, so I do not have
setPreferredSize. When I look at the code in Component it appears to me
that if there is not already a cached prefSize it just returns the size,
which I have explicitly set.
Ah well. I suppose this is just part of the joy which is Swing!

Signature
Jim Cobban jcobban@magma.ca
34 Palomino Dr.
Kanata, ON, CANADA
K2M 1M1
+1-613-592-9438
Andrei Kouznetsov - 03 Nov 2004 10:58 GMT
> public void paint(Graphics g) {
> g.setColor(getBackground());
[quoted text clipped - 4 lines]
>
> } // class LayoutColumnRenderer
I didn't understood why you don't want to use DefaultListCellRenderer (your
paint code does nothing special)?

Signature
Andrei Kouznetsov
http://uio.dev.java.net Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilitie
s
Jim Cobban - 03 Nov 2004 16:24 GMT
> > public void paint(Graphics g) {
> > g.setColor(getBackground());
[quoted text clipped - 7 lines]
> I didn't understood why you don't want to use DefaultListCellRenderer (your
> paint code does nothing special)?
I simplified the example so as to eliminate the need to show and explain a
number of internal fields in the class.