2017-05-20 91 views
3

我打印简单的使用值简单的for循环追加JTextArea,当我运行它,它的正常运行,如果我在控制台输出打印值...为什么我的线程在Swing中无法正常工作?

但是,如果我在文本区附加JTextArea和打印值,它们会在整个程序运行后追加。

public class SwingThread { 

private JFrame frame; 

/** 
* Launch the application. 
*/ 
public static void main(String[] args) { 
    EventQueue.invokeLater(new Runnable() { 
     public void run() { 
      try { 
       SwingThread window = new SwingThread(); 
       window.frame.setVisible(true); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    }); 
} 

/** 
* Create the application. 
*/ 
public SwingThread() { 
    initialize(); 
} 

/** 
* Initialize the contents of the frame. 
*/ 
private void initialize() { 
    frame = new JFrame(); 
    frame.setBounds(100, 100, 450, 300); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

    JScrollPane scrollPane = new JScrollPane(); 
    frame.getContentPane().add(scrollPane, BorderLayout.CENTER); 

    JTextArea textArea = new JTextArea(); 
    scrollPane.setViewportView(textArea); 

    JButton btnNewButton = new JButton("New button"); 
    scrollPane.setColumnHeaderView(btnNewButton); 
    btnNewButton.addActionListener(new ActionListener() 
    { 
     public void actionPerformed(ActionEvent arg0) 
     { 
      try 
      { 
       for(int i = 0 ; i <= 5 ; i++) 
       { 
        textArea.append("Value "+i+"\n"); 
        System.out.println("Value is" + i); 
        Thread.sleep(1000); 
       } 
      } 
      catch(Exception e) 
      { 
       System.out.println("Error : "+e); 
      } 
     } 
    }); 
} 
} 

我想一个接一个追加,但在整个程序运行后追加。

+1

这很容易用计时器解决,如答案所示。但是对于更复杂的处理,你需要一个后台线程 – efekctive

+1

@efekctive *“但是对于更复杂的处理,你需要一个后台线程”*如果通过复杂你的意思是一个任务需要明显的时间来执行,那么'SwingWorker '非常适合做这件事。一个swing工作者使用一个单独的线程,但也提供了简单的方法将GUI更新放回到EDT上,在那里他们应该完成。 –

+0

请参阅编辑以回答 –

回答

5

您的问题与您使用Thread.sleep相同,因为当您在Swing事件线程(或事件调度线程的EDT)上调用此函数时,它将使整个Swing事件线程进入睡眠状态。发生这种情况时,此线程的操作无法执行,包括绘制GUI(更新它)并与用户交互,这会完全冻结您的GUI - 不好。目前情况下的解决方案是使用Swing Timer作为伪循环。 Timer在后台线程中创建一个循环,并保证其actionPerformed方法中的所有代码都将在Swing事件线程上被调用,这是必要的,因为我们不想将此线程附加到JTextArea。正如其他人所指出的,如果你只想在Swing中延迟执行重复动作,那么是的,使用这个Swing Timer。另一方面,如果您希望在Swing中运行一段长时间的代码,那么此代码将再次阻止EDT并冻结您的程序。对于这种情况,请使用后台线程,例如由SwingWorker提供的后台线程。有关详情,请查看Lesson: Concurrency in Swing

例如,

btnNewButton.addActionListener(new ActionListener() { 
    public void actionPerformed(ActionEvent arg0) { 
     // delay between timer ticks: 1000 
     int timerDelay = 1000; 
     new Timer(timerDelay, new ActionListener() { 
      private int counter = 0; 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       // timer's stopping condition 
       if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5 
        ((Timer) e.getSource()).stop(); 
       } else { 
        textArea.append("Value " + counter + "\n"); 
       } 
       counter++; // increment timer's counter variable 
      } 
     }).start(); 
    } 
}); 

整个事情:

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.event.*; 

import javax.swing.*; 

public class SwingThread2 { 
    protected static final int MAX_VALUE = 5; // our constant 
    private JFrame frame; 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       try { 
        SwingThread2 window = new SwingThread2(); 
        window.frame.setVisible(true); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    public SwingThread2() { 
     initialize(); 
    } 

    private void initialize() { 
     frame = new JFrame(); 
     // frame.setBounds(100, 100, 450, 300); // avoid this 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     JScrollPane scrollPane = new JScrollPane(); 
     frame.getContentPane().add(scrollPane, BorderLayout.CENTER); 

     JTextArea textArea = new JTextArea(15, 40); 
     scrollPane.setViewportView(textArea); 

     JButton btnNewButton = new JButton("New button"); 
     scrollPane.setColumnHeaderView(btnNewButton); 
     btnNewButton.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent arg0) { 
       // delay between timer ticks: 1000 
       int timerDelay = 1000; 
       new Timer(timerDelay, new ActionListener() { 
        private int counter = 0; 
        @Override 
        public void actionPerformed(ActionEvent e) { 
         // timer's stopping condition 
         if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5 
          ((Timer) e.getSource()).stop(); 
         } else { 
          textArea.append("Value " + counter + "\n"); 
         } 
         counter++; // increment timer's counter variable 
        } 
       }).start(); 
      } 
     }); 

     // better to avoid setting sizes but instead to 
     // let the components size themselves vis pack 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
    } 
} 

只是为了更多的信息,这里是上面说的相同程序的示例使用的SwingWorker执行长时间运行的动作,和然后使用此操作更新JProgressBar。该工作人员非常简单,只需使用while循环以有限的随机数推进计数器变量。然后它传输使用这个值来更新它自己的进度属性(一个值只能从0到100,所以在其他情况下,这个值需要被归一化以符合这个)。我将一个PropertyChangeListener附加到工作人员,并且每当工作人员的进度值更改时以及每当SwingWorker更改状态时(例如完成操作时),都会在Swing事件线程上通知。在后一种情况下,工人的StateValue变成StateValue.DONE。监听者然后相应地更新GUI。如有问题,请询问。

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.event.*; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.util.Random; 
import java.util.concurrent.ExecutionException; 

import javax.swing.*; 

public class SwingThread2 { 
    protected static final int MAX_VALUE = 5; // our constant 
    private JFrame frame; 
    private JProgressBar progressBar = new JProgressBar(); 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       try { 
        SwingThread2 window = new SwingThread2(); 
        window.frame.setVisible(true); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 

    public SwingThread2() { 
     initialize(); 
    } 

    private void initialize() { 
     frame = new JFrame(); 
     // frame.setBounds(100, 100, 450, 300); // avoid this 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     JScrollPane scrollPane = new JScrollPane(); 
     frame.getContentPane().add(scrollPane, BorderLayout.CENTER); 

     JTextArea textArea = new JTextArea(15, 40); 
     scrollPane.setViewportView(textArea); 

     JButton btnNewButton = new JButton("New button"); 
     scrollPane.setColumnHeaderView(btnNewButton); 
     btnNewButton.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent arg0) { 
       // delay between timer ticks: 1000 
       int timerDelay = 1000; 
       new Timer(timerDelay, new ActionListener() { 
        private int counter = 0; 

        @Override 
        public void actionPerformed(ActionEvent e) { 
         // timer's stopping condition 
         if (counter >= MAX_VALUE) { // MAX_VALUE is a constant 
                // int = 5 
          ((Timer) e.getSource()).stop(); 
         } else { 
          textArea.append("Value " + counter + "\n"); 
         } 
         counter++; // increment timer's counter variable 
        } 
       }).start(); 
      } 
     }); 

     progressBar.setStringPainted(true); 
     JPanel bottomPanel = new JPanel(); 
     bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS)); 
     bottomPanel.add(new JButton(new MyAction("Press Me"))); 
     bottomPanel.add(progressBar); 

     frame.getContentPane().add(bottomPanel, BorderLayout.PAGE_END); 

     // better to avoid setting sizes but instead to 
     // let the components size themselves vis pack 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
    } 

    private class MyAction extends AbstractAction { 
     public MyAction(String name) { 
      super(name); 
      int mnemonic = (int) name.charAt(0); 
      putValue(MNEMONIC_KEY, mnemonic); 
     } 

     public void actionPerformed(ActionEvent e) { 
      progressBar.setValue(0); 
      setEnabled(false); 
      MyWorker myWorker = new MyWorker(); 
      myWorker.addPropertyChangeListener(new WorkerListener(this)); 
      myWorker.execute(); 
     } 
    } 

    private class WorkerListener implements PropertyChangeListener { 
     private Action action; 

     public WorkerListener(Action myAction) { 
      this.action = myAction; 
     } 

     @Override 
     public void propertyChange(PropertyChangeEvent evt) { 
      if ("progress".equals(evt.getPropertyName())) { 
       int progress = (int) evt.getNewValue(); 
       progressBar.setValue(progress); 
      } else if ("state".equals(evt.getPropertyName())) { 
       if (evt.getNewValue() == SwingWorker.StateValue.DONE) { 
        action.setEnabled(true); 

        @SuppressWarnings("rawtypes") 
        SwingWorker worker = (SwingWorker) evt.getSource(); 
        try { 
         // always want to call get to trap and act on 
         // any exceptions that the worker might cause 
         // do this even though get returns nothing 
         worker.get(); 
        } catch (InterruptedException | ExecutionException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
    } 

    private class MyWorker extends SwingWorker<Void, Void> { 
     private static final int MULTIPLIER = 80; 
     private int counter = 0; 
     private Random random = new Random(); 

     @Override 
     protected Void doInBackground() throws Exception { 
      while (counter < 100) { 
       int increment = random.nextInt(10); 
       Thread.sleep(increment * MULTIPLIER); 
       counter += increment; 
       counter = Math.min(counter, 100); 
       setProgress(counter); 
      } 
      return null; 
     } 
    } 
} 
+1

Niiice。我在编辑之前提高了评价,但现在更好了。我甚至注意到GUI被打包到正确的大小,因此不需要猜测所需的大小。 –

+0

@AndrewThompson:谢谢,但JTextArea目前的大小是通过使用“幻数”。下一次迭代将摆脱那些。 –

+0

@AndrewThompson:另外,令人印象深刻的是,OP是一位相对新手,他用一个有效的,甚至是体面的MCVE发布了他的问题,我相信应该给他奖励(和加赞)。 –

相关问题