SwingWorker ProgressBar

Kyle picture Kyle · Nov 28, 2013 · Viewed 14.3k times · Source

I am trying to get a progress bar to accurately reflect my SwingWorker. But I really can't figure out how to do it. I got the bar to just do a static animation until the operation has completed but I want a real active bar.

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package frglauncher;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

/**
 *
 * @author KYLE-LAPTOP
 */
class DownloadWorker extends SwingWorker<String, Object> {

    private String game;
    private JProgressBar bar;
    private JLabel label;

    public DownloadWorker(JProgressBar bar, String game, JLabel label) {
        this.game = game;
        this.bar = bar;
        this.label = label;
    }

    @Override
    public String doInBackground() {

        // Download here
        label.setText("test");
        try {
            // ProgressBar/Install
            System.out.println("FILELOCATION:\n----------");
            String URL_LOCATION = "http://www.futureretrogaming.tk/gamefiles/ProfessorPhys.jar";
            String LOCAL_FILE = ("\\" + game + "\\");
            File localfile = new File(LOCAL_FILE);
            if (localfile.exists()) {
                System.out.println("Directory exists!");
            }
            else {
                System.out.println("Directory doesn't exist! Creating...");
                localfile.mkdir();
                if (localfile.exists()) {
                    System.out.println("Directory created!");
                }
            }
            System.out.println("LOCALFILE:\n-------");
            System.out.println(LOCAL_FILE);
            URL website = new URL(URL_LOCATION);
            ReadableByteChannel rbc = Channels.newChannel(website.openStream());
            FileOutputStream fos = new FileOutputStream(LOCAL_FILE + "\\ProfessorPhys.jar\\");
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
            System.out.println("--------\nDone Downloading\n---------");

            RandomAccessFile randomAccessFile = null;

            File file = new File(LOCAL_FILE + "ProfessorPhys.jar\\");
            JarFile jar = new JarFile(file);
            Enumeration enum1 = jar.entries();
            while (enum1.hasMoreElements()) {
                JarEntry file1 = (JarEntry) enum1.nextElement();
                System.out.println("Directory to extract: " + LOCAL_FILE);
                System.out.println("\n" + file1.getName() + "\n");
                File f = new File(file1.getName());
                if (file1.isDirectory()) { // If it's a directory, create it
                    f.mkdir();
                    continue;
                }
                try (InputStream is1 = jar.getInputStream(file1)) {
                    FileOutputStream fos1 = new FileOutputStream(f);
                    while (is1.available() > 0) {  // Write contents of 'is' to 'fos'
                        fos1.write(is1.read());
                    }
                    fos1.close();
                }
            }
        }
        catch (FileNotFoundException ex) {
            Logger.getLogger(DownloadWorker.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (MalformedURLException ex) {
            Logger.getLogger(DownloadWorker.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex) {
            Logger.getLogger(DownloadWorker.class.getName()).log(Level.SEVERE, null, ex);
        }
        return "done";
    }

    @Override
    protected void done() {

        // Done
        label.setText("Download of " + game + "is done.");
        System.exit(0);
    }
}

Answer

ryvantage picture ryvantage · Nov 28, 2013

Several things:

  1. There are four rules to follow with SwingWorker. You can refer to this diagram: enter image description here.

So, this code:

@Override
public String doInBackground() {
    //download here
    label.setText("test");

violates that rule. Your label.setText() should be moved to the constructor.

  1. To send "updates" to Swing components (like your progress bar) you want to use the process() method, which you invoke using publish() from inside your doInBackground(). Your second SwingWorker parameter reflects the type of value you want to pass. I've attached two SSCCEs. One passes an Integer to the process() method, the other passes a String. Should give you an idea of what's going on.

SSCCE using Integer:

import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

/**
 *
 * @author Ryan
 */
public class Test {

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

    public static void go() {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JLabel label = new JLabel("Loading...");
        JProgressBar jpb = new JProgressBar();
        jpb.setIndeterminate(false);
        int max = 1000;
        jpb.setMaximum(max);
        panel.add(label);
        panel.add(jpb);
        frame.add(panel);
        frame.pack();
        frame.setSize(200,90);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        new Task_IntegerUpdate(jpb, max, label).execute();

    }

    static class Task_IntegerUpdate extends SwingWorker<Void, Integer> {

        JProgressBar jpb;
        int max;
        JLabel label;
        public Task_IntegerUpdate(JProgressBar jpb, int max, JLabel label) {
            this.jpb = jpb;
            this.max = max;
            this.label = label;
        }

        @Override
        protected void process(List<Integer> chunks) {
            int i = chunks.get(chunks.size()-1);
            jpb.setValue(i); // The last value in this array is all we care about.
            System.out.println(i);
            label.setText("Loading " + i + " of " + max);
        }

        @Override
        protected Void doInBackground() throws Exception {
            for(int i = 0; i < max; i++) {
                Thread.sleep(10); // Illustrating long-running code.
                publish(i);
            }
            return null;
        }

        @Override
        protected void done() {
            try {
                get();
                JOptionPane.showMessageDialog(jpb.getParent(), "Success", "Success", JOptionPane.INFORMATION_MESSAGE);
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }   
}

SSCCE using String:

import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

/**
 *
 * @author Ryan
 */
public class Test2 {

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

    public static void go() {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JLabel label = new JLabel("Loading...");
        JProgressBar jpb = new JProgressBar();
        jpb.setIndeterminate(true);
        panel.add(label);
        panel.add(jpb);
        frame.add(panel);
        frame.pack();
        frame.setSize(200,90);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        new Task_StringUpdate(label).execute();
    }

    static class Task_StringUpdate extends SwingWorker<Void, String> {

        JLabel jlabel;
        public Task_StringUpdate(JLabel jlabel) {
            this.jlabel = jlabel;
        }

        @Override
        protected void process(List<String> chunks) {
            jlabel.setText(chunks.get(chunks.size()-1)); // The last value in this array is all we care about.
            System.out.println(chunks.get(chunks.size()-1));
        }

        @Override
        protected Void doInBackground() throws Exception {

            publish("Loading Step 1...");
            Thread.sleep(1000);
            publish("Loading Step 2...");
            Thread.sleep(1000);
            publish("Loading Step 3...");
            Thread.sleep(1000);
            publish("Loading Step 4...");
            Thread.sleep(1000);

            return null;
        }

        @Override
        protected void done() {
            try {
                get();
                JOptionPane.showMessageDialog(jlabel.getParent(), "Success", "Success", JOptionPane.INFORMATION_MESSAGE);
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}