从单个“控制”面板重新绘制多个JPanel

时间:2012-04-09 03:01:43

标签: multithreading swing jpanel

所以我试图设置一个应用程序,其中我在jframe中有多个面板。我们可以说其中3个纯粹用于显示目的,其中一个用于控制目的。我使用borderLayout,但我不认为布局应该真正影响这里的事情。

我的问题是:我希望三个显示面板的重新绘制在控制面板中的按钮控制之下,并且我希望每当按下控制面板上的按钮时它们都同步执行。要做到这一点,我设置了这个小方法:

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连续重新粉刷,直到我再次按下按钮。问题是,当我执行循环时,消息以无限循环打印,但没有任何面板执行任何操作,即,它们都没有重新绘制。

我一直在阅读事件发送线程和摆动多线程,但到目前为止我找到的任何内容都没有真正解决我的问题。有人可以告诉我我在这里做错了什么,或者甚至更好,一些处理我所描述的情况的示例代码?感谢...

3 个答案:

答案 0 :(得分:2)

java.util.concurrent包为并发编程提供了非常强大的工具。

在下面的代码中,我使用了ReentrantLock(与Java synchronized关键字非常相似,确保多个线程对单个代码块进行互斥访问)。 ReentrantLock提供的另一件好事是Conditions,允许Threads在继续之前等待特定事件。

此处,RepaintManager只是循环播放,在repaint()上调用JPanel。但是,当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();
            }
        }
    }
}

答案 1 :(得分:1)

jComponent.getTopLevelAncestor().repaint();

答案 2 :(得分: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元素。