Hi all. I have written a card game using Swing and wanted to use a few small animations here and there to clarify what is going on in a few cases where cards are hidden or revealed. We're talking very simple animations, nothing more than some cards sliding together into one stack or sliding back apart.
The problem is, I can't seem to get any sort of animation working because my GUI isn't repainted until the entirety of the animation is finished. That is, if I animate a card sliding to the right by calling setBounds with increasing X coordinates, all that I SEE happening is that the card suddenly jumps to the right after all of the setBounds calls have finished.
As follows is a simplified mockup that should replicate the structure of my game. The container for a single object may seem unnecessary but there is more going on in the real thing. Same goes for the AnimatedThing class that's little more than a JLabel.
This is my first Swing GUI ever, so if I'm doing something that doesn't make sense, it's because I've cobbled together several examples to get the thing up and running. Please do let me know if there is a more sensible way to do any of this.
The main class...
public class AnimationExample { public AnimationExample() {} public static void main(String[] args) { final AnimationExample example = new AnimationExample(); javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run(){AnimationViewer.disp();}}); } }
A viewer class that sets up a pane and listens for a mouse click before starting the animation...
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class AnimationViewer extends JPanel implements MouseListener { public JLayeredPane pane; private AnimatedThingsContainer animatedThingsContainer; public AnimationViewer() { setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); pane = new JLayeredPane(); pane.setPreferredSize(new Dimension(200, 200)); add(pane); pane.addMouseListener(this); AnimatedThing thing = new AnimatedThing(); pane.add(thing); animatedThingsContainer = new AnimatedThingsContainer(thing); } public static void disp() { //Create and set up the window. JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. AnimationViewer view = new AnimationViewer(); JScrollPane scrollPane = new JScrollPane(view.pane); frame.setContentPane(scrollPane); //Display the window. frame.pack(); frame.setVisible(true); } public void mouseClicked(MouseEvent e) { System.out.println("animating..."); animatedThingsContainer.animate(); } public void mouseExited(MouseEvent e){} public void mouseEntered(MouseEvent e){} public void mouseReleased(MouseEvent e){} public void mousePressed(MouseEvent e){} }
A container for an AnimatedThing that handles the logic of where the AnimatedThing should go.
public class AnimatedThingsContainer { private AnimatedThing animatedThing; public AnimatedThingsContainer(AnimatedThing thing) { animatedThing = thing; } public void animate() { int currentX = 0; int currentY = 0; for(int i = 0; i < 50; ++i) { System.out.println("moving one pixel..."); animatedThing.setBounds(currentX, currentY, AnimatedThing.width, AnimatedThing.height); currentX++; animatedThing.validate(); animatedThing.repaint(); try{Thread.sleep(50);}catch(Exception e){System.out.println("oh noes");} } System.out.println("done."); } }
Finally, the AnimatedThing itself.
import javax.swing.*; public class AnimatedThing extends JLabel { public static int width = 50; public static int height = 50; public AnimatedThing() { setIcon(createImageIcon("test.gif")); setVerticalAlignment(JLabel.BOTTOM); setOpaque(false); setBounds(0, 0, width, height); } /* createImaceIcon() method taken from The Java Tutorials: How to Use Icons.*/ public static ImageIcon createImageIcon(String path) { java.net.URL imgURL = AnimatedThing.class.getResource(path); if (imgURL != null) { return new ImageIcon(imgURL); } else { System.err.println("Couldn't find file: " + path); return null; } } }
As you can see, I am calling validate and repaint after moving the AnimatedThing, and I am sleeping to give the user time to see the animation and hopefully to give the drawing thread time to repaint, but nothing seems to work. All I see when I click is the AnimatedThing jumping suddenly to the right after all of the movements are done.
Thanks for any help you can offer!
Edit: I am using Java version 1.6, if that is relevant.