Home > Net >  Display labels from array in different locations in Java
Display labels from array in different locations in Java

Time:11-06

I am trying to make elements of an array to be displayed in different locations with random time periods (with fade in and fade out effect). What i ve done so far, made an array of text labels. Made transitions. But i can't figure out, how to create a for loop that will display other labels in different locations on JFrame. And they should not appear all at the same time but one after another.

Please, help out?

Here is my code:

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

public class FadingLabel {

  private int alpha = 255;
  private int increment = -5;
  public JLabel label = new JLabel("Fading Label");
  public JLabel label2 = new JLabel("Fading Label 2");
  public JLabel label3 = new JLabel("Fading Label 3");
  public JLabel label4 = new JLabel("Fading Label 4");
  JLabel labels[] = new JLabel[]{ label, label2, label3 };

  Dimension size = label.getPreferredSize();

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

      public void run() {
        new FadingLabel().makeUI();
      }
    });
  }

  public void makeUI() {
    new Timer(80, new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        for (int i = 0; i <= 3; i  ){
          alpha  = increment;
          if (alpha >= 255) {
            alpha = 255;
            increment = -increment;
          }
          if (alpha <= 0) {
            try {
              Thread.sleep(100);
            } catch (InterruptedException interruptedException) {
              interruptedException.printStackTrace();
            }
            alpha = 0;
            increment = -increment;
          }
          label3.setForeground(new Color(0, 0, 0, alpha));
          label3.setLocation(50,60);
        }
      }
    }).start();

    JFrame frame = new JFrame();
    frame.add(labels[2]);
    frame.setPreferredSize(new Dimension(700,500));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

CodePudding user response:

Animation is hard, good animation is harder.

A stepped animation (like you've done) is not particularly efficient and can suffer from interruptions (from the OS or other parts of the system) and can be difficult to scale.

Instead, you should aim for a "duration" based animation. Where something happens over a given period of time, this way, you can more easily drop frames you can't render.

One of the difficult concepts to get around is the idea that you can't perform long running or blocking operations in the "main thread" of the GUI, but neither can you update UI from outside of the "main thread" (in Swing this is known as the Event Dispatching Thread).

So, instead, you need some way monitor each label and as it fades out, start the next label fading in. This is where a good observer pattern (AKA listener) comes in.

import java.awt.AlphaComposite;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.EventListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;

public class Main {

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private FadableLabel[] labels = new FadableLabel[]{
            new FadableLabel("A long time ago"),
            new FadableLabel("in a galaxy far, far, away..."),
            new FadableLabel("It is a period of civil war."),
            new FadableLabel("Rebel spaceships striking from a hidden base,"),
            new FadableLabel("have won their first victory against the evil Galactic Empire"),
            new FadableLabel("During the battle,"),
            new FadableLabel("Rebel spies managed to steal secret plans to the Empire's ultimate weapon,"),
            new FadableLabel("the Death Star")
        };

        private int labelIndex = -1;

        public TestPane() {
            setBorder(new EmptyBorder(50, 50, 50, 50));

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            for (FadableLabel label : labels) {
                label.setAlpha(0);
                add(label, gbc);
            }
        }

        @Override
        public void addNotify() {
            super.addNotify();
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    nextLabel();
                }
            });
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
        }

        protected void nextLabel() {
            labelIndex  ;
            if (labelIndex >= labels.length) {
                return;
            }

            FadableLabel label = labels[labelIndex];
            label.addFadableLableListener(new FadableLableListener() {
                @Override
                public void didFadeLabelIn(FadableLabel label) {
                    Timer timer = new Timer(1000, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            label.fadeOut();
                        }
                    });
                    timer.setRepeats(false);
                    timer.start();
                }

                @Override
                public void didFadeLabelOut(FadableLabel label) {
                    label.removeFadableLableListener(this);
                    nextLabel();
                }
            });
            label.fadeIn();
        }

    }

    public interface FadableLableListener extends EventListener {

        public void didFadeLabelIn(FadableLabel label);

        public void didFadeLabelOut(FadableLabel label);
    }

    public class FadableLabel extends JLabel {

        private float alpha = 1.0f;
        private Timer fadeTimer;
        private FadeRange fadeRange;

        private Instant fadeStartedAt;
        private Duration desiredFadeTime = Duration.ofMillis(1000);

        public FadableLabel() {
            super();
        }

        public FadableLabel(String text) {
            super(text);
        }

        public void addFadableLableListener(FadableLableListener listener) {
            listenerList.add(FadableLableListener.class, listener);
        }

        public void removeFadableLableListener(FadableLableListener listener) {
            listenerList.remove(FadableLableListener.class, listener);
        }

        public float getAlpha() {
            return alpha;
        }

        public void setAlpha(float alpha) {
            this.alpha = alpha;
            repaint();
        }

        protected void fireDidFadeOut() {
            FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
            if (listeners.length == 0) {
                return;
            }

            for (FadableLableListener listener : listeners) {
                listener.didFadeLabelOut(this);
            }
        }

        protected void fireDidFadeIn() {
            FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
            if (listeners.length == 0) {
                return;
            }

            for (FadableLableListener listener : listeners) {
                listener.didFadeLabelIn(this);
            }
        }

        protected void stopFadeTimer() {
            if (fadeTimer != null) {
                fadeStartedAt = null;
                fadeTimer.stop();
            }
        }

        protected void startFadeTimer() {
            if (fadeRange == null) {
                throw new RuntimeException("Fade range can not be null when starting animation");
            }
            fadeStartedAt = Instant.now();
            fadeTimer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Duration runTime = Duration.between(fadeStartedAt, Instant.now());
                    double progress = Math.min(1d, Math.max(0d, runTime.toMillis() / (double) desiredFadeTime.toMillis()));
                    setAlpha(fadeRange.valueAt(progress));
                    if (progress >= 1.0) {
                        stopFadeTimer();
                        if (getAlpha() >= 1.0) {
                            fireDidFadeIn();
                        } else {
                            fireDidFadeOut();
                        }
                    }
                }
            });
            fadeTimer.start();
        }

        public void fadeIn() {
            stopFadeTimer();
            if (alpha < 1.0) {
                fadeRange = new FadeRange(alpha, 1.0f);
                startFadeTimer();
            }
        }

        public void fadeOut() {
            stopFadeTimer();
            if (alpha > 0.0) {
                fadeRange = new FadeRange(alpha, 0);
                startFadeTimer();
            }
        }

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

        protected class FadeRange {

            private float from;
            private float to;

            public FadeRange(float from, float to) {
                this.from = from;
                this.to = to;
            }

            public float getFrom() {
                return from;
            }

            public float getTo() {
                return to;
            }

            public float getDistance() {
                return getTo() - getFrom();
            }

            public float valueAt(double progress) {
                double distance = getDistance();
                double value = distance * progress;
                value  = getFrom();
                return (float) value;
            }

        }

    }
}

Now, to your next problem. A poor approach might be to use an "absolute" or "null" layout

A better choice would be to either make use of something like GridBagLayout (and possibly EmptyLayout) and randomise the Insets of the GridBagConstraints. Alternatively, you could create your own layout manager to do the job for you

See Absolute Positioning Graphic JPanel Inside JFrame Blocked by Blank Sections and Moving JPasswordField to absolute position for some ideas

A cray example

When I say animation is "hard" and it can become complicated very fast, I'm not kidding. To that I end I wrote myself an animation library, which does all the things I keep finding myself doing.

https://github.com/RustyKnight/SuperSimpleSwingAnimationFramework

So, this is an example, based on the above library, which moves the label across a random range, while it's been faded in/out. Without the above library, this kind of work would be, a lot.

import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.util.EventListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import org.kaizen.animation.Animatable;
import org.kaizen.animation.AnimatableAdapter;
import org.kaizen.animation.DefaultAnimatableDuration;
import org.kaizen.animation.curves.AnimationCurve;
import org.kaizen.animation.curves.Curves;
import org.kaizen.animation.ranges.AnimatableRange;
import org.kaizen.animation.ranges.FloatAnimatableRange;
import org.kaizen.animation.ranges.FloatRange;
import org.kaizen.animation.ranges.PointAnimatableRange;
import org.kaizen.animation.ranges.PointRange;

public class Main {

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private String[] textValues = new String[] {
            "A long time ago",
            "in a galaxy far, far, away...",
            "It is a period of civil war.",
            "Rebel spaceships striking from a hidden base,",
            "have won their first victory against the evil Galactic Empire",
            "During the battle,",
            "Rebel spies managed to steal secret plans to the Empire's ultimate weapon,",
            "the Death Star"
        };

        private int labelIndex = -1;
        // You'd need two if you wanted to do cross fades
        private FadableLabel label;

        private Random rnd = new Random();
        // The desired duration of the animation, 1 second for fade in,
        // 1 second for fade out and 1 second for delay between swicthing state
        private Duration desiredDuration = Duration.ofSeconds(3);
        // The desired animation curve (ease in/out)
        private AnimationCurve curve = Curves.SINE_IN_OUT.getCurve();
        // The movement animator
        private DefaultAnimatableDuration animator;

        public TestPane() {
            setLayout(null);
            label = new FadableLabel();
            label.setAlpha(0);
            add(label);
            label.addFadableLableListener(new FadableLableListener() {
                @Override
                public void didFadeLabelIn(FadableLabel label) {
                    Timer timer = new Timer(1000, new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            label.fadeOut();
                        }
                    });
                    timer.setRepeats(false);
                    timer.start();
                }

                @Override
                public void didFadeLabelOut(FadableLabel label) {
                    nextText();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 400);
        }

        @Override
        public void addNotify() {
            super.addNotify();
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    nextText();
                }
            });
        }

        @Override
        public void removeNotify() {
            stopAnimation();
            super.removeNotify();
        }

        protected void stopAnimation() {
            if (animator != null) {
                animator.stop();
            }
        }

        protected void nextText() {
            stopAnimation();
            labelIndex  ;
            if (labelIndex >= textValues.length) {
                return;
            }

            String text = textValues[labelIndex];
            label.setText(text);
            label.setSize(label.getPreferredSize());

            // Randomise the from and to locations
            Point from = new Point(rnd.nextInt(getWidth() - label.getSize().width), rnd.nextInt(getHeight() - label.getSize().height));
            Point to = new Point(rnd.nextInt(getWidth() - label.getSize().width), rnd.nextInt(getHeight() - label.getSize().height));

            // Generate the range
            PointRange range = new PointRange(from, to);

            // Setup an animatable range of the PointRange
            animator = new PointAnimatableRange(range, desiredDuration, curve, new AnimatableAdapter<Point>() {
                @Override
                public void animationChanged(AnimatableRange<Point> animatable) {
                    label.setLocation(animatable.getValue());
                }
            });
            label.setLocation(from);
            // Make it so
            label.fadeIn();
            animator.start();
        }

    }

    public interface FadableLableListener extends EventListener {
        public void didFadeLabelIn(FadableLabel label);
        public void didFadeLabelOut(FadableLabel label);
    }

    public class FadableLabel extends JLabel {

        private FloatAnimatableRange animator;
        private AnimationCurve curve = Curves.SINE_IN_OUT.getCurve();
        private Duration desiredDuration = Duration.ofSeconds(1);

        private float alpha = 1.0f;

        public FadableLabel() {
            super();
        }

        public FadableLabel(String text) {
            super(text);
        }

        public void addFadableLableListener(FadableLableListener listener) {
            listenerList.add(FadableLableListener.class, listener);
        }

        public void removeFadableLableListener(FadableLableListener listener) {
            listenerList.remove(FadableLableListener.class, listener);
        }

        public float getAlpha() {
            return alpha;
        }

        public void setAlpha(float alpha) {
            this.alpha = alpha;
            repaint();
        }

        protected void fireDidFadeOut() {
            FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
            if (listeners.length == 0) {
                return;
            }

            for (FadableLableListener listener : listeners) {
                listener.didFadeLabelOut(this);
            }
        }

        protected void fireDidFadeIn() {
            FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
            if (listeners.length == 0) {
                return;
            }

            for (FadableLableListener listener : listeners) {
                listener.didFadeLabelIn(this);
            }
        }

        protected void stopFadeTimer() {
            if (animator != null) {
                animator.stop();
            }
        }

        protected void startFadeTimer(FloatRange range, AnimationListener animationListener) {
            stopFadeTimer();
            animator = new FloatAnimatableRange(range, desiredDuration, curve, new AnimatableAdapter<Float>() {
                @Override
                public void animationChanged(AnimatableRange<Float> animatable) {
                    alpha = animatable.getValue();
                    repaint();
                }

                @Override
                public void animationCompleted(Animatable animator) {
                    if (animationListener != null) {
                        animationListener.animationCompleted();
                    }
                }

            });
            animator.start();
        }

        public void fadeIn() {
            stopFadeTimer();
            startFadeTimer(new FloatRange(alpha, 1f), new AnimationListener() {
                @Override
                public void animationCompleted() {
                    fireDidFadeIn();
                }
            });
        }

        public void fadeOut() {
            stopFadeTimer();
            startFadeTimer(new FloatRange(alpha, 0f), new AnimationListener() {
                @Override
                public void animationCompleted() {
                    fireDidFadeOut();
                }
            });
        }

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

        protected interface AnimationListener {
            public void animationCompleted();
        }

    }
}

The library is based on Netbeans, it wouldn't be hard to just extract the source into some other IDE.

  • Related