Search code examples
javaswingawtevent-dispatch-thread

Java swing Loader image not displaying properly?


I have tried the below code . Functionality-

  1. Click on Button
  2. It will call a method which will take some time to process.
  3. Need to display Loader image so that user can see that processing is going on.

I tried below but if the loaderLabel1.setVisible(true); before method call doesnot show image and if we comment loaderLabel1.setVisible(false); then it shows loader image after method is finished.

Will actionPerformed method not apply the visibility to Label unless it completes? If yes is there any alternative for this problem?

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class TestLoader extends JPanel{


    static ImageIcon loading1 = new ImageIcon("D:\\WORKSPACE\\STUDY\\images\\ajax-loader.gif");



    static JLabel loaderLabel1 = new JLabel(loading1, JLabel.CENTER);


    public TestLoader(){
        super(new GridLayout(0, 1));        
        System.out.println("---------TEST ------");

        JPanel submitPanel = new JPanel();
        submitPanel.add(clearMessageButton);
        this.add(submitPanel);

        JPanel LOADER_PANEL = new JPanel();
        loaderLabel1.setVisible(false);
        LOADER_PANEL.add(loaderLabel1);
        this.add(LOADER_PANEL);
    }


    JButton clearMessageButton = new JButton(new AbstractAction("Test Result") {
        @Override
        public void actionPerformed(ActionEvent arg0) {

            loaderLabel1.setVisible(true);

            TestMethod();

            loaderLabel1.setVisible(false);

        }});

    public void TestMethod(){
        System.out.println(" =========START  TestMethod =========");
        try {
            Thread.currentThread().sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(" =========complete  TestMethod =========");
    }

    /**
     * @param args
     */
    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                TestLoader pf = new TestLoader();
                pf.display();
            }
        });


    }

    private void display() {
        JFrame frame = new JFrame("TEST LOADER");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(this);
        frame.pack();
        frame.setResizable(true);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Solution

  • There is the class SwingWorker that allows you to perform Tasks in a different thread; here's an example of how your code could be changed to use a SwingWorker:

    JButton clearMessageButton = new JButton(new AbstractAction("Test Result") {
        @Override
        public void actionPerformed(ActionEvent arg0) {
            SwingWorker worker = new SwingWorker() {
    
                @Override
                protected Object doInBackground() throws Exception {
                    loaderLabel1.setVisible(true);
                    TestMethod();
                    return true;
                }
    
                public void TestMethod() {
                    System.out.println(" =========START  TestMethod =========");
                    try {
                        Thread.currentThread().sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(" =========complete  TestMethod =========");
                }
    
                protected void done() {
                    loaderLabel1.setVisible(false);
                };
            };
            worker.execute();
        }
    });
    

    Note the methods doInBackground() that does the work in the other thread and done() that is called after doInBackground() is finished.