3

At some point in my application, I want a JLabel to "flash", i.e. invert foreground and background colors repeatedly for a few iterations. I went with a SwingWorker publish/process to do so, but due to the async nature of this process the flashing might not appear very accurate depending on system load, etc. How can I do better than that?

        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();
remi
  • 3,914
  • 1
  • 19
  • 37

2 Answers2

2

Any time you use a Thread or Timer its response time will be dependent on system load. That is you can't guarantee the event will be dispatched to the millisecond, so I wouldn't worry about it.

The only thing you might be able to do is use:

label.paintImmediately(...);

This will force the label to repaint itself without using the RepaintManager.

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

Well, then pass a String or some other object that you can ignore.

camickr
  • 321,443
  • 19
  • 166
  • 288
2

Use javax.swing.TImer. An example to look at:

EDIT: Used different variables, as previously the counter variable is showing the same values.

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 );
    }
}

EDIT 2:

Regarding comment, more information, can easily be found by opening the SwingUtilities.java file on your own local machine, by moving to the location where java is installed, and finding the src.zip folder, to watch the contents of any class. Here is the contents for ( do read the second last line of comments ) 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);
}
nIcE cOw
  • 24,468
  • 7
  • 50
  • 143
  • Was even able to write it myself after your first comment! By the way, what is the difference between `EventQueue.invokeLater` and `SwingUtilities.invokeLater` ? – remi Mar 31 '15 at 15:23
  • @remi: Nothing, actually `SwingUtilities` itself internally calls `EventQueue`. – nIcE cOw Mar 31 '15 at 15:24