3

所以我正在尝试设置一个应用程序,其中我在 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 都连续重新绘制,直到我再次按下按钮。问题是,当我执行循环时,消息在无限循环中打印,但没有一个面板做任何事情,即它们都没有重新绘制。

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

4

3 回答 3

2

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

在下面的代码中,我使用了 a ReentrantLock(它的工作方式与 Javasynchronized关键字非常相似,确保多个线程对单个代码块的互斥访问)。提供的另一件好事ReentrantLockConditions,它允许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();
            }
        }
    }
}
于 2012-04-09T04:36:13.510 回答
1
jComponent.getTopLevelAncestor().repaint();
于 2012-04-09T08:22:06.250 回答
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 元素。

于 2012-04-09T04:06:49.083 回答