1

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();
            }

        });
    }

}
TonyRomero
  • 182
  • 1
  • 4
  • 14
  • 1
    Without seeing your code, it is difficult to asses what the issue is. – bradimus Nov 02 '16 at 18:58
  • 2
    Please edit your question to include a [mcve] that defines _many_ and exhibits the problem you describe. Use the approach shown [here](http://stackoverflow.com/a/25526869/230513) to manage latency. – trashgod Nov 02 '16 at 19:23
  • I updated my first post with my code, please check, thank you. – TonyRomero Nov 02 '16 at 21:50
  • can you explain more about lag? is it when scrolling the list? or when adding new item? or the app overall is lagging whenever you do anything? – Yazan Nov 03 '16 at 13:23
  • [Adding objects lag](http://i.imgur.com/QtlvXhj.gifv) and [Scroll lag](http://i.imgur.com/MWRnyyy.gifv) – TonyRomero Nov 03 '16 at 13:55
  • If every item in your list is displayed with the same height and width, you can greatly increase performance by calling setPrototypeCellValue(xx) on the JList where xx is an instance of the same class as the items contained in the list. This enables the list to calculate it's size instead of brute force gathering size information for every item contained in the list. – Palamino Nov 03 '16 at 19:01
  • Also, in your TradeListModel class, modify the add() method to replace this.fireContentsChanged(this, 0, trades.size()) with this.fireContentsChanged(this, trade.size()-1, trades.size()) as there is no reason to flag the entire list as having changed because only the last item actually changed – Palamino Nov 03 '16 at 19:10
  • How can I print my list in reverse using the Overrided `add` method I defined? – TonyRomero Nov 04 '16 at 15:28
  • Call add(0, item) on your ArrayList instead of calling add(item). This will add the newest items at the top of the list instead of at the bottom. – Palamino Nov 04 '16 at 16:08
  • Will indexes be the same as using `add()` or will they be in reverse as well? – TonyRomero Nov 04 '16 at 16:23
  • The top item in the ArrayList is always at index 0 – Palamino Nov 04 '16 at 16:37
  • Then how can I pass the correct index to remove a given index from the same list but in reverse? Is my `remove()` method correct? – TonyRomero Nov 04 '16 at 17:08

0 Answers0