改进我的 JLabel 闪烁

Improve my JLabel flashing

在我的应用程序中的某个时刻,我想要一个 JLabel "flash",即反复反转前景色和背景色进行几次迭代。 我使用 SwingWorker publish/process 来完成此操作,但由于此过程的异步性质,根据系统负载等因素,闪烁可能看起来不太准确。我怎样才能做得更好?

        SwingWorker<Void, Void> flash = new SwingWorker<Void, Void>()
    {

        final int NUM_FLASH = 5;
        final long DELAY_MS = 500;
        @Override
        protected Void doInBackground() throws Exception
        {
            try {
                for (int i = 0; i < 2*NUM_FLASH; ++i) {
                    TimeUnit.MILLISECONDS.sleep(DELAY_MS);
                    // by the way, publish((Void[])null) throws an exception
                    publish(new Void[]{});
                }
            } catch (InterruptedException e) {
                logger.warn("Exception raised in swingworker flash ", e);
            }
            return null;
        }

        @Override
        protected void process(List<Void> chunks)
        {
            logger.debug("Swapping colors for flash");
            Color fg = label.getForeground();
            Color bg = label.getBackground();
            label.setForeground(bg);
            label.setBackground(fg);
        }

    };
    flash.execute();

任何时候您使用线程或定时器,其响应时间都将取决于系统负载。那就是你不能保证事件会被分派到毫秒,所以我不会担心。

您唯一能做的就是使用:

label.paintImmediately(...);

这将强制标签在不使用 RepaintManager 的情况下自行重绘。

// by the way, publish((Void[])null) throws an exception

好吧,然后传递一个字符串或其他一些您可以忽略的对象。

使用javax.swing.TImer。要查看的示例:

编辑: 使用了不同的变量,因为之前 counter 变量显示相同的值。

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

public class LabelExample {

    private Timer timer;
    private JButton button;
    private JLabel label;
    private Color[] labelColors = {
        Color.red,
        Color.blue
    };

    private ActionListener timerAction = new ActionListener () {
        private int counter1 = 0;
        private int counter2 = 1;
        @Override
        public void actionPerformed ( ActionEvent ae ) {
            ++counter1;
            counter1 %= labelColors.length;
            label.setBackground ( labelColors [ counter1 ] );
            System.out.println ( "Background Counter: " + counter1 + " Length: " + labelColors.length);
            ++counter2;
            counter2 %= labelColors.length;
            label.setForeground ( labelColors [ counter2 ]);
            System.out.println ( "Foreground Counter: " + counter2 + " Length: " + labelColors.length);
        }
    };

    public LabelExample () {
    }

    private void displayGUI () {
        JFrame frame = new JFrame ( "Label Example" );
        frame.setDefaultCloseOperation ( JFrame.DISPOSE_ON_CLOSE );

        JPanel contentPane = new JPanel ();

        label = new JLabel ( "Hello World!" );
        label.setOpaque ( true );
        label.setBackground ( labelColors [ 0 ] );
        label.setForeground ( labelColors [ 1 ] );

        button = new JButton ( "Stop Timer" );
        button.addActionListener ( new ActionListener () {
            @Override
            public void actionPerformed ( ActionEvent ae ) {
                timer.stop ();
            }
        } );

        contentPane.add ( label );
        contentPane.add ( button );

        frame.setContentPane ( contentPane );
        frame.pack ();
        frame.setLocationByPlatform ( true );
        frame.setVisible ( true );

        timer = new Timer ( 1000, timerAction );
        timer.start ();
    }

    public static void main ( String[] args ) {
        Runnable runnable = new Runnable () {
            @Override
            public void run () {
                new LabelExample ().displayGUI ();
            }
        };
        EventQueue.invokeLater ( runnable );
    }
}

编辑 2:

关于评论,可以很容易地找到更多信息,方法是在您自己的本地计算机上打开 SwingUtilities.java 文件,移动到安装 java 的位置,然后找到 src.zip文件夹,观看任意class的内容。这是内容(请阅读评论的倒数第二行)SwingUtilities.invokeLater ( ... ):

/**
 * Causes <i>doRun.run()</i> to be executed asynchronously on the
 * AWT event dispatching thread.  This will happen after all
 * pending AWT events have been processed.  This method should
 * be used when an application thread needs to update the GUI.
 * In the following example the <code>invokeLater</code> call queues
 * the <code>Runnable</code> object <code>doHelloWorld</code>
 * on the event dispatching thread and
 * then prints a message.
 * <pre>
 * Runnable doHelloWorld = new Runnable() {
 *     public void run() {
 *         System.out.println("Hello World on " + Thread.currentThread());
 *     }
 * };
 *
 * SwingUtilities.invokeLater(doHelloWorld);
 * System.out.println("This might well be displayed before the other message.");
 * </pre>
 * If invokeLater is called from the event dispatching thread --
 * for example, from a JButton's ActionListener -- the <i>doRun.run()</i> will
 * still be deferred until all pending events have been processed.
 * Note that if the <i>doRun.run()</i> throws an uncaught exception
 * the event dispatching thread will unwind (not the current thread).
 * <p>
 * Additional documentation and examples for this method can be
 * found in
 * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How to Use Threads</a>,
 * in <em>The Java Tutorial</em>.
 * <p>
 * As of 1.3 this method is just a cover for <code>java.awt.EventQueue.invokeLater()</code>.
 * <p>
 * Unlike the rest of Swing, this method can be invoked from any thread.
 *
 * @see #invokeAndWait
 */
public static void invokeLater(Runnable doRun) {
    EventQueue.invokeLater(doRun);
}