Hi,
Is there a way to make a lazy loading with Swing JList ?
Cheers,
Hi,
Is there a way to make a lazy loading with Swing JList ?
Cheers,
In a way, yes. You can create a custom ListModel
which uses the getElementAt(int index)
method to load the correct value if it hasn't already been loaded. See the example in the Javadocs for JList
:
// This list model has about 2^16 elements. Enjoy scrolling.
ListModel bigData = new AbstractListModel() {
public int getSize() { return Short.MAX_VALUE; }
public Object getElementAt(int index) { return "Index " + index; }
};
Just to add to the other answer, when you create your own implementation of ListModel
, while you are loading the data you will want to call:
fireIntervalAdded(Object source,int index0, int index1)
Assuming that you are loading data into your list incrementally. This will cause the JList
that is using it as a model to update.
Incorrect. That JList above is NOT lazily loaded.
Swing insists on accessing each item in the entire ListModel while getting it displayed on screen. Furthermore, after accessing all the items, Swing then re-accesses the first n number of items visible on screen (in the viewport, not off screen below).
Run this simple "TestJList" class to prove it. I call println each time 'getElementAt' is executed. You can plainly see that Swing calls that method for every item in the ListModel.
This happens for me on a MacBook unibody running Mac OS X 10.6.2 with Java:
"1.6.0_17" Java(TM) SE Runtime Environment (build 1.6.0_17-b04-248-10M3025) Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01-101, mixed mode)
import javax.swing.*;
/**
* This example proves that a JList is NOT lazily-loaded.
*/
public class TestJList {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create an artificial ListModel.
ListModel bigData =
new AbstractListModel() {
public int getSize() {
// return Short.MAX_VALUE; // Try this if you have a long while to waste.
return 10;
}
public Object getElementAt(int index) {
System.out.println("Executing 'getElementAt' # " + index);
return "Index " + index;
}
};
// Create a JList.
JList myList = new JList(bigData);
// Add the JList to the frame.
frame.getContentPane().add(myList);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(
new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Run that code, and you'll see:
Executing 'getElementAt' # 0
Executing 'getElementAt' # 1
Executing 'getElementAt' # 2
Executing 'getElementAt' # 3
Executing 'getElementAt' # 4
Executing 'getElementAt' # 5
Executing 'getElementAt' # 6
Executing 'getElementAt' # 7
Executing 'getElementAt' # 8
Executing 'getElementAt' # 9
Executing 'getElementAt' # 0
Executing 'getElementAt' # 1
Executing 'getElementAt' # 2
Executing 'getElementAt' # 3
Executing 'getElementAt' # 4
Executing 'getElementAt' # 5
Executing 'getElementAt' # 6
Executing 'getElementAt' # 7
Executing 'getElementAt' # 8
Executing 'getElementAt' # 9
-fin-
I solved it. I had missed the solution discussed at the top of the JList API documentation.
In the example source code I posted in another answer on this topic, add this line (and comment) after creating the JList:
// Tell JList to test rendered size using this one value rather than every item in ListModel. (Much faster initialization) myList.setPrototypeCellValue("Index " + Short.MAX_VALUE);
The problem is that JList, by default, is accessing every item in the entire ListModel to determine at runtime the necessary display size. The line added above overrides that default, and tells JList to examine just the one value as passed. That one value acts as a template (prototype) for sizing the display of the JList.
See:
http://java.sun.com/javase/6/docs/api/javax/swing/JList.html#prototype_example
-fin-