Open source Java projects: AnimatingCardLayout

Animated transitions for your Java GUIs

1 2 3 Page 2
Page 2 of 3

Suppose that you are designing a shopping-cart application whose GUI consists of order and payment screens, and you want to transition the user from the order screen to the payment screen after the user fills out the order details. In the following code fragment, this GUI is represented via two cards. One of these cards is for the order screen and the other card is for the payment screen:

JPanel cards = new JPanel ();
acl = new AnimatingCardLayout (new FadeAnimation ());
cards.setLayout (acl);

JPanel order = createOrderScreen ();
cards.add (order, "order");
JPanel payment = createPaymentScreen ();
cards.add (payment, "payment");

acl.show (cards, "order");

After creating a cards container and an AnimatingCardLayout instance associated with a fade transition effect, and after associating this layout manager with the container, the code fragment creates order and payment card panels populated with appropriate components, and invokes the manager's show() method to ensure that the order screen is displayed before payment.

Presumably, you would include a component such as a button on the order screen for switching to the payment screen when the component's listener is invoked. For example, when the button's action listener is invoked, it could execute the following statement to replace the order screen with the payment screen via the previously specified fade transition effect:

acl.show (cards, "payment");

A slideshow with animated transitions

To demonstrate the usefulness of AnimatingCardLayout, I have created a SlideShow application that presents a slideshow. This application reads GIF and JPEG images from a specific directory, displays each image slide in sequence, and employs this layout manager with randomly chosen transition effects to transition between successive slides. Listing 1 presents SlideShow's source code.

Listing 1. SlideShow.java

// SlideShow.java

import java.awt.*;
import java.awt.event.*;

import java.io.*;

import java.util.ArrayList;

import javax.swing.*;

import org.javadev.*;
import org.javadev.effects.*;

public class SlideShow extends JFrame
{
   final static int ANIM_DUR = 2500;
   final static int DEFAULT_WINDOW_SIZE = 500;
   final static int TIMER_DELAY = 6000;

   AnimatingCardLayout acl;

   Animation [] animations =
   {
      new CubeAnimation (),
      new DashboardAnimation (),
      new FadeAnimation (),
      new IrisAnimation (),
      new RadialAnimation (),
      new SlideAnimation ()
   };

   static ArrayList<ImageIcon> images = new ArrayList<ImageIcon> ();

   boolean showStartupMessage = true;

   int index;

   JPanel pictures;

   Timer timer;

   public SlideShow ()
   {
      super ("Slide Show");
      setDefaultCloseOperation (EXIT_ON_CLOSE);

      pictures = new JPanel ();
      pictures.setBackground (Color.black);

      acl = new AnimatingCardLayout ();
      acl.setAnimationDuration (ANIM_DUR);
      pictures.setLayout (acl);

      JLabel picture = new JLabel ();

      picture.setHorizontalAlignment (JLabel.CENTER);
      pictures.add (picture, "pic1");

      picture = new JLabel ();
      picture.setHorizontalAlignment (JLabel.CENTER);
      pictures.add (picture, "pic2");

      ActionListener al;
      al = new ActionListener ()
           {
               public void actionPerformed (ActionEvent ae)
               {
                  if (index == images.size ())
                  {
                      timer.stop (); // End the slideshow
                      return;
                  }

                  acl.setAnimation (animations [(int) (Math.random ()*
                                                animations.length)]);

                  if ((index & 1) == 0) // Even indexes
                  {
                      JLabel pic = (JLabel) pictures.getComponent (1);
                      pic.setIcon (images.get (index++));
                      try
                      {
                          acl.show (pictures, "pic2");
                      }
                      catch (IllegalStateException  ise)
                      {
                          index--; // Retry picture on next timer invocation
                      }
                  }
                  else // Odd indexes
                  {
                      JLabel pic = (JLabel) pictures.getComponent (0);
                      pic.setIcon (images.get (index++));
                      try
                      {
                          acl.show (pictures, "pic1");
                      }
                      catch (IllegalStateException  ise)
                      {
                          index--; // Retry picture on next timer invocation
                      }
                  }
               }
           };

      setContentPane (pictures);

      timer = new Timer (TIMER_DELAY, al);
      timer.start ();

      setSize (DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE);
      setVisible (true);
   }

   public void paint (Graphics g)
   {
      super.paint (g);

      if (showStartupMessage)
      {
          g.setColor (Color.yellow);
          g.drawString ("One moment please...", 30, 60);
          showStartupMessage = false;
      }
   }

   public static void main (String [] args)
   {
      if (args.length != 1)
      {
          System.err.println ("usage: java SlideShow imagePath");
          return;
      }

      final File imagePath = new File (args [0]);
      if (!imagePath.isDirectory ())
      {
          System.err.println (args [0]+" is not a directory path");
          return;
      }

      Runnable r = new Runnable ()
                   {
                       public void run ()
                       {
                          // Load all GIF and JPEG images in the imagePath.

                          File [] filePaths = imagePath.listFiles ();
                          for (File filePath: filePaths)
                          { 
                               if (filePath.isDirectory ())
                                   continue;

                               String name;
                               name = filePath.getName ().toLowerCase ();
                               if (name.endsWith (".gif") ||
                                   name.endsWith (".jpg"))
                               {
                                   System.out.println ("Loading "+filePath);

                                   ImageIcon ii;
                                   ii = new ImageIcon (filePath.toString ());
                                   images.add (ii);
                               }
                          }

                          if (images.size () < 2)
                          {
                              System.err.println ("too few images");
                              System.exit (0);
                          }

                          new SlideShow ();
                       }
                   };
      EventQueue.invokeLater (r);
   }
}

About SlideShow.java

SlideShow requires a single command-line argument, which is the path to a directory that contains at least two GIF and/or JPEG files. At startup, this application creates an ImageIcon object for each GIF/JPEG, and stores these objects in an ArrayList. During the slideshow, each of these objects is read from this collection, and its image is displayed via a transition effect.

Images are displayed from within the action listener of a timer that controls the slideshow. Each timer pulse invokes the listener, which randomly assigns a transition effect to the layout manager, assigns the next ImageIcon to one of the manager's pic1 and pic2 card label components, and invokes show() to transition from the current card label image to the other card label image.

Images associated with even ArrayList indexes are displayed via the pic2 card's label; images with odd indexes are displayed via pic1's label. This allows the very first image (at index 0) to be displayed via a transition effect. If I associated pic1 with even indexes (and so on), the first image would appear without a transition effect (a layout manager idiosyncrasy).

The timer pulses every few milliseconds as set by TIMER_DELAY. At least some of these milliseconds (set by ANIM_DUR) are used by the transition effect -- setAnimationDuration() assigns this value to the AnimatingCardLayout instance. Although ANIM_DUR is much smaller than TIMER_DELAY, it is possible for the transition effect to occupy too much time.

On slow machines (or machines with limited amounts of RAM -- not much of a problem these days), a transition effect might occasionally take much more time to complete than the value specified by ANIM_DUR. In fact, it is possible that another timer pulse might occur before the effect finishes. If this happens, AnimatingCardLayout's overridden show() method will throw an IllegalStateException.

This exception is thrown because show() is not re-entrant: A transition effect must end before this method can be re-invoked. On my Windows XP platform, this has not been a problem. However, to be on the safe side, I wrap each of the timer action listener's show() method calls in a try/catch construct. If the exception is thrown, the slideshow retries the image on the next timer pulse.

1 2 3 Page 2
Page 2 of 3