0

I can make a single JLabel fade in, but how do I make multiple labels fade in one after another? Here is my current code.

public void fadeIn(final JLabel jLabel) {
    jLabelList.add(jLabel);
    jLabelBackPanel.add(jLabelList.get(jLabelList.size() - 1));
    new SwingWorker<Void, Void>() {
        @Override
        protected Void doInBackground() throws Exception {
            int c = 0;
            for(int i = 0; i < 255; i++) {
                Thread.sleep(1000);
                jLabel.setForeground(new Color(
                    jLabel.getForeground().getRed(),
                    jLabel.getForeground().getGreen(),
                    jLabel.getForeground().getBlue(),
                    c++));
            }
            return null;
        }
    }.execute();
}

The above code will fade in about 9 labels at one time, then 9 more, etc. I can't figure out how to make one label wait until the last one is done fading in.

CompSci-PVT
  • 1,303
  • 2
  • 10
  • 23
  • Are the labels in a single container? For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). – Andrew Thompson Jun 11 '14 at 09:43
  • 4
    1. 8miliseconds is very short period, is under latency in Native OS, 2. never to use Thread.sleep(8); in Java nor in Swing, block an EDT, 3. use Swing Timer, 4. Swing Worker is about bridge to Workers Thread, don't to use for animations in Swing, use Swing Timer instead – mKorbel Jun 11 '14 at 09:57
  • 2
    For [example](http://stackoverflow.com/a/2234020/230513). – trashgod Jun 11 '14 at 10:15
  • @AndrewThompson Yes, I am placing the JLabels in a JPanel with BoxLayout w/ Y-axis so they stack. The effect is much like a scrolling text area but I have the added convenience of manipulating JLabels instead of the text area document. – CompSci-PVT Jun 11 '14 at 11:22
  • 1
    You shouldn't be modify the states of UI components from any thread other then the Event Dispatching Thread – MadProgrammer Jun 11 '14 at 11:34

1 Answers1

4

Now, there's probably a few ways this might be done, but this is basically what I've come up with...

First, I created a custom label, which uses a javax.swing.Timer to progress from a starting alpha value to a target alpha value (ie 0-1 to fade in).

To this label, I added a simply waitFor method, which waits until the target value has been reached. This is achieved through a simple object monitor. Very important, NEVER call this method while you're on the Event Dispatching Thread...

Next, I created a series of these labels with the text I wanted displayed and added each one to to the output.

I then started a separate Thread and iterated the list, fading each label in and using waitFor to wait for it to be displayed...

Fading

import java.awt.AlphaComposite;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class FadingLabels {

    public static void main(String[] args) {
        new FadingLabels();
    }

    public FadingLabels() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                final List<FadingLabel> labels = new ArrayList<>(25);
                labels.add(new FadingLabel("A "));
                labels.add(new FadingLabel("long "));
                labels.add(new FadingLabel("time "));
                labels.add(new FadingLabel("ago "));
                labels.add(new FadingLabel("in "));
                labels.add(new FadingLabel("a "));
                labels.add(new FadingLabel("galaxy "));
                labels.add(new FadingLabel("far, "));
                labels.add(new FadingLabel("far, "));
                labels.add(new FadingLabel("away"));
                labels.add(new FadingLabel("..."));

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());

                for (FadingLabel label : labels) {
                    frame.add(label);
                }

                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (FadingLabel label : labels) {
                            label.fadeIn();
                            label.waitFor();
                        }
                    }
                }).start();

            }
        });
    }

    public class FadingLabel extends JLabel {

        protected static final int TIME = 1000;
        protected final Object fadeLock = new Object();

        private float targetAlpha;
        private float alpha = 0;
        private Timer timer;
        private long startTime;
        private float fromAlpha;

        public FadingLabel() {
            init();
        }

        public FadingLabel(String text, Icon icon, int horizontalAlignment) {
            super(text, icon, horizontalAlignment);
            init();
        }

        public FadingLabel(String text, int horizontalAlignment) {
            super(text, horizontalAlignment);
            init();
        }

        public FadingLabel(String text) {
            super(text);
            init();
        }

        public FadingLabel(Icon image, int horizontalAlignment) {
            super(image, horizontalAlignment);
            init();
        }

        public FadingLabel(Icon image) {
            super(image);
            init();
        }

        protected void init() {

            timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (alpha < 1f) {
                        long now = System.currentTimeMillis();
                        long diff = now - startTime;
                        float progress = (float) diff / (float) TIME;

                        float distance = targetAlpha - fromAlpha;
                        alpha = (float) (distance * progress);
                        alpha += fromAlpha;

                        if (alpha > 1f) {
                            timer.stop();
                            alpha = 1f;
                        }

                    } else {
                        alpha = 1f;
                        timer.stop();
                    }
                    repaint();
                    if (!timer.isRunning()) {
                        synchronized (fadeLock) {
                            fadeLock.notifyAll();
                        }
                    }
                }
            });
            timer.setInitialDelay(0);

        }

        protected void fadeTo(float target) {
            Runnable run = new Runnable() {
                @Override
                public void run() {
                    timer.stop();
                    fromAlpha = alpha;
                    targetAlpha = target;
                    if (targetAlpha != alpha) {
                        startTime = System.currentTimeMillis();
                        timer.start();
                    } else {
                        repaint();
                    }
                }
            };
            if (EventQueue.isDispatchThread()) {
                run.run();
            } else {
                EventQueue.invokeLater(run);
            }
        }

        public void fadeIn() {
            fadeTo(1f);
        }

        public void fadeOut() {
            fadeTo(0f);
        }

        public void waitFor() {
            if (EventQueue.isDispatchThread()) {
                throw new IllegalStateException("Calling waitFor while within the EDT!");
            }
            synchronized (fadeLock) {
                try {
                    fadeLock.wait();
                } catch (InterruptedException ex) {
                }
            }
        }

        @Override
        public void paint(Graphics g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
            super.paint(g2d);
            g2d.dispose();
        }

    }

}

The animation portion uses a fixed time progression, rather then a fixed delta. This allows the animation to be more variable depending on the over heads that might be occurred from the OS. Basically what this means is that the animation will progress over a fixed period of time each time

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • @ MadProgrammer +1 just for showing the animation! It looks exactly like what I want to do. I will try out your example code and see if it works in my program. – CompSci-PVT Jun 11 '14 at 11:36