2012-04-09 81 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();