caffeine_inquisitor
caffeine_inquisitor

Reputation: 727

Swing - JList contents disappear randomly on dynamic update

It has been a while since the last time I did Swing programming, and today I'm getting back to it. I have a simple JList which is backed by DefaultListModel. I also have a JButton which will show a JFileChooser. When a directory is selected, the JList is supposed to be populated with the file names under the selected directory.

What I found is that occasionally (actually it happens randomly quite often), the list wont be updated until I click on the (seemingly blank) list. I thought by using DefaultListModel, I can just call addElement() which will trigger the fireIntervalAdded (which should repaint the list, the container, etc) ? ALso, I believe the actionPerformed() method is invoked inside the EDT, so I should just be able to update the DefaultListModel. Anyway.... I have also tried calling revalidate() and repaint() on the list, the container, etc without any success either.

Secondly, when the list already has some items in it, clicking the button (which triggers the filechooser to be shown) will clear up the JList entries (without calling clear() on the model).

The source code is available at:

https://github.com/alexwibowo/spider

Here is an abstract of the code (hopefully it is sufficient)

  package org.github.alexwibowo.spider.gui;

  import com.jgoodies.forms.factories.CC;
  import com.jgoodies.forms.layout.FormLayout;

   import javax.swing.*;

  public class MainPanel extends JPanel {
  public MainPanel() {
    initComponents();
  }

private void initComponents() {
    toolBar1 = new JToolBar();
    openFolderButton = new JButton();
    splitPane1 = new JSplitPane();
    scrollPane1 = new JScrollPane();
    fileList = new JList();

    //======== this ========
    setLayout(new FormLayout(
        "default:grow",
        "default, $lgap, fill:default:grow"));

    //======== toolBar1 ========
    {
        toolBar1.setFloatable(false);

        //---- openFolderButton ----
        openFolderButton.setIcon(UIManager.getIcon("Tree.openIcon"));
        openFolderButton.setBorder(new EmptyBorder(5, 5, 5, 5));
        toolBar1.add(openFolderButton);
    }
    add(toolBar1, CC.xy(1, 1));

        //======== splitPane1 ========
          {

            //======== scrollPane1 ========
            {

            //---- fileList ----
            fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            scrollPane1.setViewportView(fileList);
        }
        splitPane1.setLeftComponent(scrollPane1);
    }
    add(splitPane1, CC.xy(1, 3));
}

protected JToolBar toolBar1;
protected JButton openFolderButton;
protected JSplitPane splitPane1;
protected JScrollPane scrollPane1;
protected JList fileList;

  }

and the panel which extends the above. This is the class which handles the addition of filenames to the list :

  package org.github.alexwibowo.spider.gui

import javax.swing.*
import java.awt.event.ActionEvent
import java.awt.event.ActionListener

class BarcodeMainPanel extends MainPanel {
private DefaultListModel<String> listModel = new DefaultListModel<String>()

BarcodeMainPanel() {
    initModels()
    initEventHandling()
}

protected void initModels() {
    fileList.model = listModel
}

protected void initEventHandling() {
    openFolderButton.addActionListener(new ActionListener() {
        @Override
        void actionPerformed(ActionEvent e) {
                    JFileChooser chooser = new JFileChooser();
                    chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                    chooser.setLocation(50, 50);
                    if (chooser.showOpenDialog(BarcodeSpiderMainFrame.instance()) == JFileChooser.APPROVE_OPTION) {
                        listModel.clear()
                        File selectedDirectory = chooser.getSelectedFile()
                        selectedDirectory.eachFile {
                            listModel.addElement(it.name)
                        }
                    } else {
                        System.out.println("No Selection ");
                    }
                }
    })
}
   }

The frame which contains the panel (just for completeness) :

package org.github.alexwibowo.spider.gui

import groovy.transform.Synchronized

import javax.swing.*
import java.awt.*

class BarcodeSpiderMainFrame extends JFrame{

    private static BarcodeSpiderMainFrame INSTANCE;

    BarcodeSpiderMainFrame(String title) throws HeadlessException {
        super(title)
    }

    @Synchronized
    public static BarcodeSpiderMainFrame instance() {
        if (INSTANCE == null) {
            INSTANCE = new BarcodeSpiderMainFrame("Spider")
            INSTANCE.minimumSize = new Dimension(800,600)
            INSTANCE.maximumSize = new Dimension(1024,768)
            INSTANCE.defaultCloseOperation = EXIT_ON_CLOSE
        }
        INSTANCE.initializeContent()
        INSTANCE.visible = true
        INSTANCE
    }

    private void initializeContent() {
        BarcodeMainPanel mainPanel = new BarcodeMainPanel()
        this.contentPane.add(mainPanel);
    }
}

and finally the launcher (just for completeness) :

package org.github.alexwibowo.spider

import org.github.alexwibowo.spider.gui.BarcodeSpiderMainFrame

import javax.swing.*

@Singleton
class SpiderLauncher {
    BarcodeSpiderMainFrame barcodeSpiderMainFrame

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SpiderLauncher.instance.run(args);
            }
        });
    }

    void run(String[] args) {
        barcodeSpiderMainFrame = BarcodeSpiderMainFrame.instance()
        barcodeSpiderMainFrame.show()
    }
}

Upvotes: 0

Views: 547

Answers (1)

caffeine_inquisitor
caffeine_inquisitor

Reputation: 727

This is what fixes it. In BarcodeSpiderMainFrame, remove the call to setVisible. So it will look something like:

public static BarcodeSpiderMainFrame instance() {
    if (INSTANCE == null) {
        INSTANCE = new BarcodeSpiderMainFrame("Spider")
        INSTANCE.minimumSize = new Dimension(800,600)
        INSTANCE.preferredSize = new Dimension(1024,768)
        INSTANCE.maximumSize = new Dimension(1024,768)
        INSTANCE.defaultCloseOperation = EXIT_ON_CLOSE
    }

    INSTANCE.initializeContent()
     // INSTANCE.visible = true   // remove this line       
    INSTANCE
}

and in the launcher, call setVisible()

@Singleton
class SpiderLauncher {
    BarcodeSpiderMainFrame barcodeSpiderMainFrame

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SpiderLauncher.instance.run(args);
            }
        });
    }

    void run(String[] args) {
        barcodeSpiderMainFrame = BarcodeSpiderMainFrame.instance()
        barcodeSpiderMainFrame.pack()
        barcodeSpiderMainFrame.setVisible(true) // add this line
    }
}

I have added the call to pack(). But i dont think it really matters. How did the above fix my problem? I do not know. It would be great if someone can explain what actually happened.

Upvotes: 1

Related Questions