0

I have got a set of nodes in my program, each have a specific x,y location. and each have a set of image icons.

I want to draw image animation for each nodes at its specific location.

Here is my code: (this only shows the last image which i know why!.)

public void showPicture()  {
        //nodes : 
        for(int i=0;i<thisGraph.getNode().size();i++){
            if(thisGraph.getNode().get(i).getImageIcon()!=(null)){
                for(int j=0;j<thisGraph.getNode().get(i).getImageIcon().size();j++){
                    if(j>0)
                       lables.get(lables.size()-1).setVisible(false);
                    JLabel jLabel1 = new JLabel();
                    lables.add(jLabel1);
                    jLabel1.setLayout(new GridBagLayout());
                    jLabel1.setIcon(thisGraph.getNode().get(i).getImageIcon().get(j));
                    jLabel1.setVisible(true);
                    jLabel1.setBounds((int)thisGraph.getNode().get(i).getX(),(int)thisGraph.getNode().get(i).getY(),195,163);           
                    jPanel1.add(jLabel1);

                }
            }
        }
    }

This method showPicture() is called in a buttonActionListener. And I also have another button which I want it to stop the image animations for all labels.

What I have tried: Thread.sleep() -> it freezes the button and it only shows the last image

I figured I had to use timer, but through all the topics I went they only used it on one label, not multiple labels.

Edit -> i read those examples given in the comments . and here is what i have resolved but it still is freezes the button and doesn't works :

int j = 0;
    public void showPicture(){
        //nodes : 
        for(int i=0;i<thisGraph.getNode().size();i++){
            if(thisGraph.getNode().get(i).getImageIcon()!=(null)){
              j=0;
                while( j<thisGraph.getNode().get(i).getImageIcon().size()){

                    if(j>0)
                        lables.get(lables.size()-1).setVisible(false);
                    JLabel jLabel1 = new JLabel();
                    lables.add(jLabel1);
                    jLabel1.setLayout(new GridBagLayout());
                    jLabel1.setIcon(thisGraph.getNode().get(i).getImageIcon().get(j));
                    jLabel1.setVisible(true);
                    jLabel1.setBounds((int)thisGraph.getNode().get(i).getX(),(int)thisGraph.getNode().get(i).getY(),195,163);

                    jPanel1.add(jLabel1);

                    //

                    ActionListener act;
                    act = new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            jLabel1.setVisible(true);        
                            j++;
                        }
                    };
                    Timer timer = new Timer(1000, act );
                    timer.start();

                    timer.stop();
                    //

                }
            }
        }}
Ava
  • 41
  • 8
  • *"what i have tried : Thread.sleep() -> it freezes the button"*. As it has been discussed multiple times in the forum, `Thread.sleep()` freezes the app because it stops the EDT. You need to use a Swing Timer, [for example...](https://stackoverflow.com/questions/41877789/animated-sprites-with-java-swing/41879444#41879444) – Frakcool Jun 01 '18 at 19:30
  • Why it shows the last picture only? Because `showPicture()` method will run all the `for` loops and just show the last one. To correct this, again a `Swing Timer` would help, [here](https://stackoverflow.com/a/46792479/2180785) is another example with a similar problem to the one you have – Frakcool Jun 01 '18 at 19:32
  • 1
    If after reading both examples and investigating you still cannot solve your problem, post a proper [mcve] with the improvements done after reading the above examples – Frakcool Jun 01 '18 at 19:59
  • i read those examples and i edited my text . please see it again @Frakcool – Ava Jun 01 '18 at 20:16
  • 1
    Did you read about the [mcve]? What part didn't you understand? Your example is far from being **complete**. *"but it still is freezes the button and doesn't works"* You're still using a `for` loop and a `while` loop inside it. You start the timer but stop it right after it starts.. – Frakcool Jun 01 '18 at 20:18
  • Create ONE Swing `Timer`. This will act as the "main-loop". Each time the `Timer` ticks, you will loop through the `List` of labels and perform what ever task you need. When you want to stop the animation, you call `stop` on the ONE `Timer`, when you want to start the animation, you call `start` on the ONE `Timer` – MadProgrammer Jun 01 '18 at 21:23

1 Answers1

1

Swing is single threaded and not thread safe. This means that you shouldn't block the Event Dispatching Thread with long running or blocking operations, like Thread.sleep. You should also, only ever update the UI (or anything it relies on) from within the context of the Event Dispatching Thread.

See Concurrency in Swing for more details.

Probably the simplest solution to your problem is to use a Swing Timer.

The idea is a you use a single Timer to act as the "main animation loop", changing the properties of ALL the objects you need updated within it.

The following is pretty basic example, it animates 100 JLabels, simply changing their background color with a randomly picked color

Sparkle

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class Test {

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

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

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private List<JLabel> nodes = new ArrayList<>(100);

        private Random random = new Random();
        private Color[] colors = new Color[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, Color.MAGENTA};

        public TestPane() {
            setLayout(new GridLayout(0, 10));
            for (int index = 0; index < 100; index++) {
                JLabel label = new JLabel();
                label.setBorder(new EmptyBorder(5, 5, 5, 5));
                label.setOpaque(true);
                label.setBackground(pickColor());
                nodes.add(label);
                add(label);
            }
            Timer timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (JLabel label : nodes) {
                        label.setBackground(pickColor());
                    }
                }
            });
            timer.start();
        }

        protected Color pickColor() {
            return colors[random.nextInt(colors.length)];
        }

    }

}

See How to Use Swing Timers for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366