如何在Swing中实现“进行中”动画?

时间:2012-08-30 23:24:46

标签: java multithreading swing

我正在开发一个执行一些长时间运行的函数的应用程序。为了让用户知道正在进行处理,我需要一个标签,可以显示一些可以代表该标签的标签。所以,我为这样的标签创建了一个小部件。

下面的程序运行find,我得到了我想要的输出。

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


/**
 * This is an extension to a JLabel that can be used to display an ongoing progress.
 * @author Ankit Gupta
 */
public class ProgressLabel extends JLabel {

    /**
     * The prefix label to which periods are added.
     */
    private String startLabel;
    /**
     * The label to display end of an operation.
     */
    private String endLabel;

    /**
     * Flag to indicate whether the animation is running or not.
     */
    private boolean running = false;
    //list to hold intermediate labels
    List<String> intermediateLabels;

    public ProgressLabel(String slbl, String elbl) {
        this.startLabel = slbl;
        this.endLabel = elbl;
        //initialize all the labels to be used once as creating them again and again is expensive
        intermediateLabels = new ArrayList<String>();
        intermediateLabels.add(startLabel+".");
        intermediateLabels.add(startLabel+"..");
        intermediateLabels.add(startLabel+"...");
        intermediateLabels.add(startLabel+"....");
    }

    public void done(){
        running = false;
    }

    public void start(){
        running = true;
        new LabelUpdateThread().start();
    }

    private class LabelUpdateThread extends Thread{
        int i;

        public LabelUpdateThread(){
            i=0;
        }

        @Override
        public void run(){
            while(running){
                SwingUtilities.invokeLater(new Runnable(){
                    @Override
                    public void run() {
                        setText(intermediateLabels.get((i++)%3));
                    }
                });

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {}
            }
            setText(endLabel);
        }
    }

    public static void main(String []args) throws InterruptedException{
        final JFrame frame = new JFrame("Testing ProgressLabel");
        JPanel panel = new JPanel();
        ProgressLabel progressLabel = new CZProgressLabel("Searching", "Done");
        panel.add(progressLabel);
        frame.add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(500,500));
        frame.pack();
        progressLabel.start();
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                frame.setVisible(true);
            } 
        });
        Thread.sleep(5000);
        progressLabel.done();
    }
}

但是,当我尝试将其包含在应用程序中时,它没有按预期工作。我创建了一个带有按钮的小面板,在按钮的actionPerfomed()代码中我使用了ProgressLabel start() done()方法和以前一样,但这次,标签只是在长度处理完成之前没有更新到 Done 。以下是使用ProgressLabelactionPerformed()的另一段代码:

public class SearchPanel extends JPanel {

    private JTextArea queryBox;
    private JButton searchBtn;
    private ProgressLabel progressLabel;
    private JSeparator queryAreaSeparator;

    public SearchPanel() {
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();

        //First Row
        gbc.gridy = 0;
        gbc.gridwidth = 2;
        gbc.gridx = 0;
        queryBox = new JTextArea();
        queryBox.setRows(25);
        queryBox.setColumns(25);
        this.add(queryBox, gbc);


        //Second Row
        gbc.gridy = 1;

        gbc.gridwidth = 1;
        progressLabel = new ProgressLabel("Searching", "Done");
        this.add(progressLabel, gbc);

        gbc.gridx = 1;
        searchBtn = new JButton("Search");
        this.add(searchBtn, gbc);
        searchBtn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                progressLabel.start();
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException ex) {
                    Exceptions.printStackTrace(ex);
                }
                //the above sleep() call will be replace by some time-consuming process. It is there just for testing now

                progressLabel.done();
            }
        });


        gbc.gridx = 0;
    }

    /**
     * function to test CZSemanticSearchLabel
     */
    public static void main(String[] args) throws InterruptedException {
        final JFrame frame = new JFrame();
        CZSemanticSearchPanel panel = new CZSemanticSearchPanel();
        frame.add(panel);
        frame.setPreferredSize(new Dimension(500, 500));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        Thread.sleep(10000);
        frame.dispose();


        final JFrame frame1 = new JFrame("Testing ProgressLabel");
        JPanel panel1 = new JPanel();
        CZProgressLabel progressLabel = new CZProgressLabel("Searching", "Done");
        panel1.add(progressLabel);
        frame1.add(panel1);
        frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame1.setPreferredSize(new Dimension(500, 500));
        frame1.pack();
        progressLabel.start();
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                frame1.setVisible(true);
            }
        });
        Thread.sleep(5000);
        progressLabel.done();
    }
}

我相信我已经用Swing的事件调度模型搞砸了。但是,我无法想象什么?有人能告诉我这段代码有什么问题,我该如何纠正呢?

2 个答案:

答案 0 :(得分:3)

您可以使用针对此类内容制作的SwingWorker: Simple Background Tasks,而不是使用线程自行实现此功能,并且链接的示例与您的问题非常相似。

您的start()未执行LabelUpdateThread().run(),而是LabelUpdateThread().start()

答案 1 :(得分:3)

我不知道您的实际代码,但您的示例代码存在缺陷......

ActionListener你正在做这件事......

progressLabel.start();
try {
    Thread.sleep(10000);
} catch (InterruptedException ex) {
    Exceptions.printStackTrace(ex);
}
//the above sleep() call will be replace by some time-consuming process. It is there just for testing now

progressLabel.done();

这将停止 Event Dispatching Thread,防止处理任何重绘请求(即没有屏幕更新)10秒......这也会使您的应用程序看起来像“挂”。

我更新了你ActionListener以便这样阅读(注意我添加了一个isRunning方法,该方法从标签中返回running成员)

if (progressLabel.isRunning()) {
    progressLabel.done();
} else {
    progressLabel.start();
}

它运作正常。

您可能希望通过Currency in Swing阅读更多想法。

此外,正如已经建议的那样,SwingWorker可能是一种更好的方法