【问题标题】:How can I show progress bar using Swingworker?如何使用 Swingworker 显示进度条?
【发布时间】:2020-01-28 19:21:31
【问题描述】:

这是我的代码 sn-p。单击按钮时,它会在后台执行加载程序,但我无法在进度栏中获取任务的详细信息。谁能告诉我我在这里缺少什么?

关键是我不想在我的 doInBackground 方法中包含所有插入代码。

public class ProgressBarDemo extends JPanel
                             implements ActionListener, 
                                        PropertyChangeListener {
private JProgressBar progressBar;
private JButton startButton;
private JTextArea taskOutput;
private Task task;

class Task extends SwingWorker<Void, Integer> {
    @Override
    protected void process(List<Integer> arg0) {
        // TODO Auto-generated method stub
        super.process(arg0);
        for(int k:arg0)
        System.out.println("arg is "+k);
        setProgress(arg0.size()-1);
    }

    /*
     * Main task. Executed in background thread.
     */
    @Override
    public Void doInBackground() throws Exception {
        Random random = new Random();
        int progress = 0;
        //Initialize progress property.
        setProgress(0);
        Thread.sleep(100);
        new LoadUnderwritingData().filesinfolder("D:\\files to upload\\");
        System.out.println("records inserted are "+LoadData.records_count_inserted);
        publish(LoadData.records_count_inserted);
        /*
         * while (progress < 100) { //Sleep for up to one second. try {
         * Thread.sleep(random.nextInt(1000)); } catch (InterruptedException ignore) {}
         * //Make random progress. progress += random.nextInt(10);
         * setProgress(Math.min(progress, 100)); }
         */
        return null;
    }

    /*
     * Executed in event dispatching thread
     */
    @Override
    public void done() {
        Toolkit.getDefaultToolkit().beep();
        startButton.setEnabled(true);
        setCursor(null); //turn off the wait cursor
        taskOutput.append("Done!\n");
    }
}

【问题讨论】:

标签: java multithreading swing desktop-application swingworker


【解决方案1】:

我看到你没有打电话给progressBar.setValue(progress);
此外,您还需要在每个插入的元素之后调用 publish(currentInsertCount);。 在你会做的过程方法中:

// assuming you are passing the current insert count to publish()
for (int k : arg0){
    System.out.println("arg is " + k);
    progressBar.setValue(k);
}

但是,从您现在发布的内容来看,甚至还不清楚处理发生在哪里。 是这一行吗:

new LoadUnderwritingData().filesinfolder("D:\\files to upload\\");

如果是,那么您必须将一些回调传递给filesinfolder("..."),以便它可以更新进度。

注意:在普通的新线程中执行此操作可能比使用SwingWorker 更容易。

我将如何使用普通线程来做到这一点:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileFilter;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class ProgressBarDemo extends JPanel implements ActionListener {

    private JProgressBar progressBar;
    private JButton startButton;
    private JTextArea taskOutput;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("ProgressBarDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().setPreferredSize(new Dimension(500, 500));
            frame.add(new ProgressBarDemo());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

    public ProgressBarDemo() {
        progressBar = new JProgressBar(0, 100);
        startButton = new JButton(">");
        taskOutput = new JTextArea();

        startButton.addActionListener(this);

        setLayout(new BorderLayout());
        add(startButton, BorderLayout.NORTH);
        add(taskOutput, BorderLayout.CENTER);
        add(progressBar, BorderLayout.SOUTH);

        progressBar.setVisible(false);
    }

    // If you don't care about a small chance of the UI not updating properly then you can perform the updates directly instead of calling invokeLater.
    // If you are compiling below Java 1.8, you need to convert Lambda expression to a Runnable and make the directory parameter final.
    public void upload(File directory) {
        // No need for invokeLater here because this is called within the AWT Event Handling
        startButton.setEnabled(false);
        progressBar.setVisible(true);
        new Thread() {

            @Override
            public void run() {
                taskOutput.append("Discovering files...\n");
                // List<File> files = Arrays.asList(directory.listFiles()); //
                // if you want to process both files and directories, but only
                // in the given folder, not in any sub folders
                // List<File> files = Arrays.asList(getAllFiles(directory)); //
                // if you only want the files in that directory, but not in sub
                // directories
                List<File> files = getAllFilesRecursive(directory); // if you
                                                                    // want all
                                                                    // files

                SwingUtilities.invokeLater(() -> {
                    taskOutput.append("  -> discovered " + files.size() + " files.\n");
                    progressBar.setMaximum(files.size());
                    taskOutput.append("Processing files...\n");
                });
                int processedCount = 0;
                for (File file : files) {
                    try {
                        byte[] bytes = Files.readAllBytes(file.toPath());
                        // TODO: process / upload or whatever you want to do with it
                    } catch (Throwable e) {
                        // Same here, you may skip the invokeLater
                        SwingUtilities.invokeLater(() -> taskOutput.append("Failed to process " + file.getName() + ": " + e.getMessage() + "\n"));
                        e.printStackTrace();
                    } finally {
                        processedCount++;
                        final int prcCont = processedCount; // not necessary if invoking directly
                        SwingUtilities.invokeLater(() -> progressBar.setValue(prcCont));
                    }
                }
                SwingUtilities.invokeLater(() -> {
                    taskOutput.append("  -> done.\n");
                    startButton.setEnabled(true);
                    progressBar.setVisible(false);
                });
            }
        }.start();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        upload(new File("C:\\directoryToUpload"));
    }

    /**
     * Gets all normal files in the directory.
     */
    public static File[] getAllFiles(File directory) {
        return directory.listFiles(new FileFilter() {

            @Override
            public boolean accept(File pathname) {
                return pathname.isFile();
            }
        });
    }

    /**
     * Gets all normal files in the given directory and its sub directories.
     */
    public static List<File> getAllFilesRecursive(File directory) {
        List<File> result = new ArrayList<File>();
        getAllFilesRecursive(directory, result);
        return result;

    }

    private static void getAllFilesRecursive(File directory, List<File> addTo) {
        if (directory.isFile()) {
            addTo.add(directory);
        } else if (directory.isDirectory()) {
            File[] subFiles = directory.listFiles();
            if (subFiles == null)
                return;
            for (File subFile : subFiles) {
                getAllFilesRecursive(subFile, addTo);
            }
        }
    }

}

【讨论】:

  • “在一个普通的新线程中而不是使用 SwingWorker 可能更容易。”startButtonprogressBar 的所有更新(等等。对于 GUI 组件)应该在 EDT 上完成。这就是为什么摇摆工人在这里更可取的原因。它有确保发生这种情况的方法。
  • 我知道,但是在使用 Swing 的 4 年里,我从来没有以安全的方式做到这一点,也从来没有遇到过问题。如果您不使用SwingUtilities.invokeLater 填充代码,我认为代码会更清晰,但我猜这是个人偏好。
  • “但是在使用 Swing 的 4 年里,我从来没有以安全的方式做到这一点,而且从来没有遇到过问题。” 你的几十个用户也是这样吗?
  • 真的不想参与任何讨论,但是:我不会将我的应用程序传递给很多人(尽管我每天都使用它们),但我也使用每秒更新 60 次的动画.因此,我假设 Swing 具有良好的异常处理/恢复。我想当你的应用程序总是正确更新是至关重要的(用户不必强制更新,例如通过调整大小),或者你正在为大量用户提供服务,牺牲干净的代码以提高可靠性。因此我会更新我的答案。我仍然认为将 SwingWorker 仅用于 ProgressBar 是一种矫枉过正。
  • 编辑:没关系,我忘了 Lambda,所以在 Java 1.8 中,即使使用 invokeLater 调用,代码看起来也很漂亮(除了关于非最终变量的小不便)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多