2010-12-15 5 views
2

J'ai besoin d'exécuter/afficher une série d'événements d'un Arraylist à un JTextArea, cependant, chaque événement est exécuté avec un temps différent. Voici un exemple rapide de mon objectif:Java Swing Affichage de la concurrence JTextArea

public void start(ActionEvent e) 
{ 
    SwingUtilities.invokeLater(new Runnable() 
    { 
    public void run() 

    { 
    jTextArea.append("Test" + "\n"); 
    try 
    { 
    Thread.sleep(3000); 
    } catch (InterruptedException e1) 
    { 
    e1.printStackTrace(); 
    } 
    jTextArea.append("Test1" + "\n"); 
    } 
    }); 
} 

Donc maintenant, « Test » et l'affichage « Test1 » sur JTextArea après l'exécution entière est terminée. Comment puis-je faire écran « Test », puis 3 secondes plus tard, l'affichage « Test1 »

Merci u tous à l'avance

Répondre

5

invokeLater planifie l'exécution Runnable sur le thread de répartition des événements. Vous ne devriez pas dormir dedans ou vous allez affamer le fil d'expédition. Essayez d'utiliser un thread de travail distinct à la place:

Thread worker = new Thread(new Runnable(){ 
    public void run(){ 
     jTextArea.append("Test" + "\n"); 
     try { 
      Thread.sleep(3000); 
     } catch (InterruptedException e1) { 
      e1.printStackTrace(); 
     } 
     jTextArea.append("Test1" + "\n"); 

    } 
}); 
worker.start(); 
+0

merci beaucoup. Ça a marché! – Jack

+0

Dans mon expérience, même si JTextArea # append est répertorié comme étant "thread-safe", et donc appelable de l'EDT, ce n'est vraiment pas le cas dans de nombreux cas, et cette méthode devrait également être appelée sur l'EDT. –

2

Comme indiqué, c'est une mauvaise idée, car vous allez bloquer le thread d'événement. Toutefois, il est également important de comprendre la raison de cette situation. Comme vous le savez, tout le code qui affecte l'état des composants Swing doit se produire dans le thread de gestion des événements (ce qui est la raison pour laquelle invokeLater et ses amis doivent toujours être utilisés).

Ce qui est un peu moins connu, c'est que le code d'exécution s'exécute également dans le thread de gestion d'événements. Lorsque votre appel à Thread.sleep s'exécute, il ne bloque pas seulement le thread d'événement, il bloque également toute peinture de composants. C'est pourquoi la mise à jour complète semble se produire en une seule fois - le JTextArea est mis à jour mais il ne peut pas être repeint jusqu'à ce que votre méthode d'exécution retourne.

Beaucoup d'informations ici: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

3

Si vos tâches sont temps/cpu intensive, alors oui, certainement utiliser un fil d'arrière-plan pour le faire comme un objet SwingWorker ou une course Runnable dans un thread. Si toutefois ce que vous devez faire est d'échelonner l'affichage de quelque chose et tout ce que vous cherchez est l'équivalent Swing de Thread.sleep (3000), alors votre meilleure option est d'utiliser une minuterie Swing. Il y a un excellent tutoriel sur la façon d'utiliser ces que vous pouvez trouver ici: http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html

Par exemple:

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.*; 

public class Fu extends JPanel { 
     private static final int TIMER_DELAY = 600; 
     protected static final int MAX_COUNT = 20; 
     private JTextArea jTextArea = new JTextArea(10, 10); 
     private JButton startBtn = new JButton("Start"); 
     private Timer timer; 

     public Fu() { 
      startBtn.addActionListener(new ActionListener() { 
       public void actionPerformed(ActionEvent e) { 
        startAction(e); 
       } 
      }); 

      add(new JScrollPane(jTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
        JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); 
      add(startBtn); 
     } 

     private void startAction(ActionEvent e) { 
      if (timer != null && timer.isRunning()) { 
       // prevent multiple instances of timer from running at same time 
       return; 
      } 
      timer = new Timer(TIMER_DELAY, new ActionListener() { 
       private int count = 0; 
       public void actionPerformed(ActionEvent e) { 
        if (count < MAX_COUNT) { 
          count++; 
          jTextArea.append("Test " + count + "\n"); 
        } else { 
          jTextArea.append("Done! \n"); 
          timer.stop(); 
          timer = null; 
        } 
       } 
      }); 
      timer.setInitialDelay(0); 
      timer.start(); 
     } 

     public static void main(String[] args) { 
      SwingUtilities.invokeLater(new Runnable() { 
       public void run() { 
        JFrame frame = new JFrame("Foo"); 
        frame.getContentPane().add(new Fu()); 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        frame.pack(); 
        frame.setLocationRelativeTo(null); 
        frame.setVisible(true); 
       } 
      }); 
     } 
}