在Swing中,如何在进行某些处理时重新绘制标签?

时间:2011-08-05 19:30:20

标签: java swing repaint

我是Swing的新手,我试图这样做:

在按下JButton时,程序将开始迭代数百个项目,每个项目处理1秒钟,在完成每个项目后,他应该更新标签以显示已处理的项目数。

问题是,在循环完成所有项目的迭代之前,标签的文本不会更新。

我在网上搜索,显然是因为它在同一个线程中运行,所以我创建了一个新线程来处理数据并更新要在标签中使用的变量(已处理文件的数量)。

但它没有用。然后我甚至创建了另一个线程,我在前一个线程之后开始,只重新绘制标签。仍然没有任何作用。

代码是这样的:

btnNewButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            try { SwingUtilities.invokeLater(validateFiles); } 
}); }

Runnable validateFiles = new Runnable() {

    @Override
    public void run() {
                 while(x_is_not_100) {
                      processLoadsOfStuff();
                      label.setText(x); }
            }
};

你能帮我解决这个问题吗?

2 个答案:

答案 0 :(得分:7)

简单 - 使用SwingWorker。有关更多信息,请阅读Tasks that Have Interim Results教程。


以下是一个非常通用的示例,它将使用JLabel来显示从 0 30 的计数 -

public final class SwingWorkerDemo {
    private static JLabel label = 
        new JLabel(String.valueOf(0), SwingConstants.CENTER);

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

        JLabelSwingWorker workerThread = new JLabelSwingWorker();
        workerThread.run();
    }

    private static void createAndShowGUI(){
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(label);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static class JLabelSwingWorker extends SwingWorker<Void, Integer>{
        @Override
        protected Void doInBackground() throws Exception {
            for(int i = 1; i < 31; i++){
                Thread.sleep(1000);
                publish(i);
            }
            return null;
        }

        @Override
        protected void process(List<Integer> integers) {
            Integer i = integers.get(integers.size() - 1);
            label.setText(i.toString());
        }
    }
}

答案 1 :(得分:6)

后台处理必须在单独的线程中完成。但标签更新必须在事件派发线程中完成。

所以你的代码应该是这样的:

btnNewButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // start a new thread for the background task
        new Thread(validateFiles).start(); 
    }); 
}

Runnable validateFiles = new Runnable() {

    @Override
    public void run() {
             while(x_is_not_100) {
                  processLoadsOfStuff();
                  // use SwingUtilities.invokeLater so that the label update is done in the EDT:
                  SwingUtilities.invokeLater(new Runnable() {
                      @Override
                      public void run() {
                          label.setText(x);
                      }
                  });
             }
    };

但您可能希望使用SwingWorker类,它旨在以更简单的方式执行此操作。它的文档做得很好,包含了一些例子。