Wednesday, July 30, 2014

Java Swing "Flash" a Component

One line of code:
    new ComponentFlasher().flashNow(myTextField);  // works on buttons too

Sometimes people need help finding messages on screen, or seeing data that has changed. The above code will change the color of the text in the text field myTextField to red, then quickly fade it back to black.

 

ComponentFlasher.java :


import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

// Please feel free to reuse, modify and distribute.  
// Please add your own url or email address to this list:
//
// - http://javajon.blogspot.com/
//

/**
 * Class to make it easy to flash a component.
 */
public class ComponentFlasher {

    private final List flashQueue = new LinkedList();
    private final Object FLASH_LOCK = new Object();
    private volatile JComponent flashCurrent = null;
    private final FlashTimer flashTimer = new FlashTimer();
    private final int maxQueueSize = 3;

    public void flashQueue(JComponent componentToAddToQueue) {
        synchronized (FLASH_LOCK) {
            if (flashCurrent == componentToAddToQueue) {
                flashReset(componentToAddToQueue);
            } else {
                final boolean restartTimer = flashQueue.isEmpty() && (flashCurrent == null);
                flashQueue.remove(componentToAddToQueue);
                flashQueue.add(componentToAddToQueue);
                if (restartTimer) {
                    flashTimer.start();
                }
            }
        }
    }

    public void flashNow(final JComponent componentToFlashNow) {
        synchronized (FLASH_LOCK) {
            if (flashCurrent != componentToFlashNow) {
                final boolean restartTimer;
                if (flashCurrent != null) {
                    final JComponent componentToPutBackIntoTheQueue = flashCurrent;
                    flashQueue.remove(componentToPutBackIntoTheQueue);
                    flashQueue.add(0, componentToPutBackIntoTheQueue);

                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            componentToPutBackIntoTheQueue.setForeground(Color.BLACK);
                        }
                    });

                    restartTimer = false;
                } else {
                    restartTimer = flashQueue.isEmpty();
                }
                flashCurrent = componentToFlashNow;
                if (restartTimer) {
                    flashTimer.start();
                }
            }
            flashReset(componentToFlashNow);
        }
    }

    private void flashReset(final JComponent componentToFlashNow) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                componentToFlashNow.setForeground(Color.RED);
            }
        });
    }

    private void flashIteration(final JComponent componentToFlash) {
        // run this code on the UI thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // find out how red we still are
                final int red = flashCurrent.getForeground().getRed() - 10;
                if (red > 0) {
                    componentToFlash.setForeground(new Color(red, 0, 0));
                } else {
                    synchronized (FLASH_LOCK) {
                        if (flashCurrent == componentToFlash) {
                            flashCurrent = null;
                        }
                    }
                }
            }
        });
    }

    private void checkQueue() {
        // no current component - check queue
        if (!flashQueue.isEmpty()) {
            synchronized (FLASH_LOCK) {
                // recheck the same flags we just checked
                // now that we have the lock we are sure they won't change.
                if (flashCurrent == null) {
                    if (flashQueue.isEmpty()) {
                        // stop the timer
                        flashTimer.stop();
                    } else {
                        // make the next item current
                        while (flashQueue.size() > (maxQueueSize + 1)) {
                            flashQueue.remove(0);
                        }
                        final JComponent next = flashQueue.remove(0);
                        flashCurrent = next;
                        flashReset(next);
                    }
                }
            }
        }
    }

    private class FlashTimer extends Timer {

        public FlashTimer() {
            super(50, new FlashTimerAction());
        }
    }

    private class FlashTimerAction implements ActionListener {

        public FlashTimerAction() {
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            // is there a current component ?
            if (flashCurrent == null) {
                checkQueue();
            } else {
                // current component - copy to send to anon-implementation Runnable
                flashIteration(flashCurrent);
            }
        }
    }
}


No comments:

Post a Comment