2012-04-09 75 views
3

所以我試圖設置一個應用程序,其中我有一個jframe內的多個面板。可以說其中3個純粹用於顯示目的,其中一個用於控制目的。我使用的是borderLayout,但我不認爲佈局應該真的影響這裏的東西。我想要的三個顯示面板的重新繪製在控制面板中的按鈕的控制下,並且我希望它們在按下控制面板上的按鈕時都同步執行。要做到這一點,我成立了這個小方法:從單個「控制」面板重新繪製多個JPanel

public void update(){ 
      while(ButtonIsOn){ 
       a.repaint(); 
       b.repaint() 
       c.repaint(); 
       System.out.println("a,b, and c should have repainted"); 

       } 
    } 

其中a,b和c都顯示面板,我想A,B和C的所有重繪汽車無,直到我再次按下按鈕。問題是,當我執行循環時,消息在無限循環中打印,但是沒有任何面板執行任何操作,即它們都不重繪。

我一直在閱讀關於事件調度線程和swing多線程,但我迄今發現的東西都沒有真正解決我的問題。有人能告訴我我在這裏做錯了什麼,或者甚至更好,處理我描述的情況的一些示例代碼?謝謝...

+0

*一個很好的教程*爲了更好地幫助越早,張貼[SSCCE(HTTP「..來處理我所描述的情況的一些示例代碼?」:// SSCCE。組織/)。 – 2012-04-09 08:21:16

+1

請學習java命名約定並堅持使用它們。 – kleopatra 2012-04-09 09:06:22

+1

究竟是什麼問題(除了你的無限循環,由不重置控制while的布爾值引起)?該面板的_none究竟做了什麼,即沒有一個repaint_ manifest? – kleopatra 2012-04-09 09:07:40

回答

2

java.util.concurrent包爲併發編程提供了非常強大的工具。

在下面的代碼中,我使用了一個ReentrantLock(其工作方式與Java​​關鍵字非常相似,確保多個線程對單個代碼塊進行互斥訪問)。 ReentrantLock提供的另一件好事是Conditions,允許Threads在繼續之前等待特定事件。

這裏,RepaintManager簡單循環,在JPanel上調用repaint()。但是,當調用toggleRepaintMode()時,它會阻塞,等待modeChanged Condition,直到再次調用toggleRepaintMode()

您應該能夠立即運行以下代碼。按JButton切換重繪JPanel(您可以看到System.out.println聲明工作)。

總的來說,我強烈建議您熟悉java.util.concurrent提供的功能。那裏有很多非常強大的東西。還有在http://docs.oracle.com/javase/tutorial/essential/concurrency/

import java.awt.Component; 
import java.awt.Graphics; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Collection; 
import java.util.Collections; 
import java.util.concurrent.locks.Condition; 
import java.util.concurrent.locks.ReentrantLock; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class RepaintTest { 
    public static void main(String[] args) { 

     JFrame frame = new JFrame(); 
     JPanel panel = new JPanel() 
     { 
      @Override 
      public void paintComponent(Graphics g) 
      { 
       super.paintComponent(g); 

       // print something when the JPanel repaints 
       // so that we know things are working 
       System.out.println("repainting"); 
      } 
     }; 

     frame.add(panel); 

     final JButton button = new JButton("Button"); 
     panel.add(button); 

     // create and start an instance of our custom 
     // RepaintThread, defined below 
     final RepaintThread thread = new RepaintThread(Collections.singletonList(panel)); 
     thread.start(); 

     // add an ActionListener to the JButton 
     // which turns on and off the RepaintThread 
     button.addActionListener(new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent arg0) { 
       thread.toggleRepaintMode(); 
      } 
     }); 

     frame.setSize(300, 300); 
     frame.setVisible(true); 
    } 

    public static class RepaintThread extends Thread 
    { 
     ReentrantLock lock; 
     Condition modeChanged; 
     boolean repaintMode; 
     Collection<? extends Component> list; 

     public RepaintThread(Collection<? extends Component> list) 
     { 
      this.lock = new ReentrantLock(); 
      this.modeChanged = this.lock.newCondition(); 

      this.repaintMode = false; 
      this.list = list; 
     } 

     @Override 
     public void run() 
     { 
      while(true) 
      { 
       lock.lock(); 
       try 
       { 
        // if repaintMode is false, wait until 
        // Condition.signal() is called 
        while (!repaintMode) 
         try { modeChanged.await(); } catch (InterruptedException e) { } 
       } 
       finally 
       { 
        lock.unlock(); 
       } 

       // call repaint on all the Components 
       // we're not on the event dispatch thread, but 
       // repaint() is safe to call from any thread 
       for (Component c : list) c.repaint(); 

       // wait a bit 
       try { Thread.sleep(50); } catch (InterruptedException e) { } 
      } 
     } 

     public void toggleRepaintMode() 
     { 
      lock.lock(); 
      try 
      { 
       // update the repaint mode and notify anyone 
       // awaiting on the Condition that repaintMode has changed 
       this.repaintMode = !this.repaintMode; 
       this.modeChanged.signalAll(); 
      } 
      finally 
      { 
       lock.unlock(); 
      } 
     } 
    } 
} 
+0

謝謝。所以這種工作有點像監視器或信號量呢? – russell88 2012-04-09 15:45:58

+0

對,'ReentrantLock'非常像* monitor *或* semaphore *,計數爲1(即a * mutex *)或* synchronized *關鍵字,因爲它保證只有一個線程能夠同時訪問它所守衛的代碼塊。 – ulmangt 2012-04-09 15:57:24

+0

在這種情況下,這也很好,因爲使用'Condition'意味着'RepaintThread'類在關閉重新繪製時不必*忙等待*。它可以簡單地調用Condition.await(),然後該線程將被阻塞,不會浪費CPU週期,直到調用Condition.signal()。 – ulmangt 2012-04-09 15:59:11

0

您可以使用SwingWorker這個。 SwingWorker被設計爲在後臺執行長時間運行的任務,而不會阻塞事件分派器線程。因此,您需要擴展SwingWorker並實施某些對您有意義的方法。請注意,所有長時間運行的操作都應發生在doInBackground()方法中,並且Swing UI元素只應在done()方法中更新。

所以這裏有一個例子:現在

class JPanelTask extends SwingWorker<String, Object>{ 
    JPanel panel = null; 
    Color bg = null; 
    public JPanelTask(JPanel panel){ 
     this.panel = panel; 
    } 
    @Override 
    protected String doInBackground() throws Exception { 
     //loooong running computation. 
     return "COMPLETE"; 
    } 
    @Override 
    protected void done() { 
     panel.repaint(); 
    } 

} 

,在您的 「控制」 按鈕的執行動作的事件,你可以做到以下幾點:

controlButton.addActionListener(new ActionListener() { 

     @Override 
     public void actionPerformed(ActionEvent arg0) { 
      JPanelTask task1 = new JPanelTask(panel1); 
      task1.execute(); 
      JPanelTask task2 = new JPanelTask(panel2); 
      task2.execute(); 
      //so on.. 
     } 
    }); 

另一種方法是使用javax.swing.Timer。計時器可幫助您及時快速更改您的UI元素。這可能不是最合適的解決方案。但它也完成了工作。 同樣,你應該小心在正確的地方更新UI元素。

1
jComponent.getTopLevelAncestor().repaint();