I have made a custom ListModel, custom CellRenderer to populate a custom object JList. Every cell contains two images and three strings. This JList is populated dynamically by three threads in concurrency but first I'm testing it adding objects one by one by pressing a button. The method for adding objects to the list is invoked as an ActionListener linked to that button.
The problem is that when many objects are displayed in my list my program starts lagging so much. Then my question is: what's the best way to dinamically populate a JList and make it "lag-proof"?
Adding objects lag and Scroll lag
Main class:
public class Main extends JFrame{
/**
*
*/
private static final long serialVersionUID = 1L;
JFrame main = new JFrame();
@SuppressWarnings("rawtypes")
JList tradesList = new JList();
TradesListModel lm = new TradesListModel();
@SuppressWarnings("unchecked")
public Main(){
GUI();
tradesList.setCellRenderer(new TradeRenderer());
tradesList.setModel(lm);
}
public void GUI(){
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setMinimumSize(new Dimension(450,700));
main.setLocationRelativeTo(null);
Container pane = main.getContentPane();
pane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 1;
// BUTTONS PANEL
JPanel buttonsPanel = new JPanel();
buttonsPanel.setLayout(new GridBagLayout());
gbc.gridx = 0;
gbc.gridy = 0;
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
addTrade();
}
});
buttonsPanel.add(startButton, gbc);
gbc.gridx++;
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
lm.clear();
}
});
buttonsPanel.add(stopButton, gbc);
// LIST PANEL
JPanel listPanel = new JPanel();
listPanel.setLayout(new GridBagLayout());
listPanel.setBorder(BorderFactory.createTitledBorder("Trades List"));
JScrollPane scrollList = new JScrollPane(tradesList);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weighty = 1;
listPanel.add(scrollList, gbc);
GridBagConstraints mc = new GridBagConstraints();
mc.insets = new Insets(5,5,5,5);
mc.fill = GridBagConstraints.BOTH;
mc.weightx = 1;
mc.gridx = 0;
mc.gridy = 0;
pane.add(buttonsPanel, mc);
mc.gridy++;
mc.weighty = 1;
pane.add(listPanel, mc);
main.setVisible(true);
}
public void addTrade(){
lm.add(new Trade("userr", "Platform", "#", "Noteeeeeessss", "Site", 999));;
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Main();
}
TradesListModel class:
package window;
import java.util.ArrayList;
import javax.swing.AbstractListModel;
import main.Trade;
public class TradesListModel extends AbstractListModel<Object>{
/**
*
*/
private static final long serialVersionUID = 1L;
private ArrayList<Trade> trades;
public TradesListModel(){
trades = new ArrayList<Trade>();
}
public void add(Trade trade){
trades.add(trade);
this.fireContentsChanged(this, 0, trades.size());
}
public void remove(int index){
trades.remove(index);
}
public void clear(){
trades.clear();
this.fireIntervalRemoved(this, 0, trades.size());
}
@Override
public int getSize() {
return trades.size();
}
@Override
public Object getElementAt(int index) {
return trades.get(index);
}
}
TradeRenderer class:
package window;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import main.Trade;
public class TradeRenderer extends JPanel implements ListCellRenderer<Object>{
/**
*
*/
private static final long serialVersionUID = 1L;
private GridBagConstraints gbc;
public TradeRenderer(){
this.setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
}
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus){
Trade trade = (Trade) value;
JLabel haveLabel = new JLabel(trade.getUsername());
JLabel wantLabel = new JLabel(trade.getNotes());
gbc.gridx = 0;
gbc.gridy = 0;
this.add(haveLabel, gbc);
gbc.gridy++;
this.add(wantLabel, gbc);
setEnabled(true);
return this;
}
}
Trade class:
package main;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
public class Trade {
private String username;
private String platform;
private String url;
private ArrayList<Item> have;
private ArrayList<Item> want;
private String notes;
private String site;
private long time;
public Trade(String username, String platform, String url, String notes, String site, long time){
this.username=username;
this.platform=platform;
this.url=url;
this.notes=notes;
this.site=site;
this.time=time;
have = new ArrayList<Item>();
want = new ArrayList<Item>();
}
public void addHave(Item i){
have.add(i);
}
public void addWant(Item i){
want.add(i);
}
public String getUsername(){
return username;
}
public String getPlatform(){
return platform;
}
public String getUrl(){
return url;
}
public String getNotes(){
return notes;
}
EDIT1: I tried running my application using a SwingWorker, but it doesn't improve the latency and as many new Trade objects are added to the JList and are displayed, the GUI starts lagging. Here's the SwingWorker implementation:
private void start(){
SwingWorker<Boolean, Trade> worker = new SwingWorker<Boolean, Trade>(){
@Override
protected Boolean doInBackground() throws Exception {
for(int i=0; i<30; i++){
publish();
System.out.println("Trade added.");
}
return true;
}
@Override
protected void process(List<Trade> chunks) {
lm.add(new Trade("username", "platform", "#", "Noteeeeeessss", "Site", 999));
}
};
worker.execute();
}
The application is started by this new main class:
package window;
import javax.swing.SwingUtilities;
public class App {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
new MainFrame();
}
});
}
}