/*==========================================================================*\ | $Id: AnimatedIcon.java,v 1.2 2010/05/27 21:41:07 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | This class was published by Rob Camick on June 21, 2009 as part of | a Java Tips Weblog article called "Animated Icon": | | http://tips4java.wordpress.com/2009/06/21/animated-icon/ \*==========================================================================*/ package student; import java.awt.Component; import java.awt.Graphics; 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.JComponent; import javax.swing.Timer; //------------------------------------------------------------------------- /** * The AnimatedIcon will display a series of Icons in a predetermined * sequence. This animation sequence can be configured to keep repeating or * to stop after a specified number of cycles. * <p> * The size of the Icon is determined to be the largest width or height of * any Icon. All other Icons are then aligned within the space available when * the Icon is painted. * </p><p> * An AnimatedIcon cannot be shared by different components. However, the * Icons added to an AnimatedIcon can be shared. * </p><p> * The animation sequence is a simple sequential display of each Icon. When * the end is reached the animation restarts at the first Icon. Icons are * displayed in the order in which they are added. To create custom animation * sequences you will need to override the getNextIconIndex() and * isCycleCompleted() methods. * </p><p> * This class was published by Rob Camick on June 21, 2009 as part of * a Java Tips Weblog article called "Animated Icon": * </p><p> * http://tips4java.wordpress.com/2009/06/21/animated-icon/ * </p> * * @author Rob Camick (via a Java Tips Weblog article) */ public class AnimatedIcon implements Icon, ActionListener, Runnable { private final static int DEFAULT_DELAY = 500; private final static int DEFAULT_CYCLES = -1; /** A constant representing alignment with the top edge of a container. */ public final static float TOP = 0.0f; /** A constant representing alignment with the left edge of a container. */ public final static float LEFT = 0.0f; /** A constant representing alignment in the middle of a container. */ public final static float CENTER = 0.5f; /** * A constant representing alignment with the bottom edge of a * container. */ public final static float BOTTOM = 1.0f; /** * A constant representing alignment with the right edge of a * container. */ public final static float RIGHT = 1.0f; private JComponent component; private List<Icon> icons = new ArrayList<Icon>(); private int cycles; private boolean showFirstIcon = false; private float alignmentX = CENTER; private float alignmentY = CENTER; // Track the X, Y location of the Icon within its parent JComponent so we // can request a repaint of only the Icon and not the entire JComponent private int iconX; private int iconY; // Used for the implementation of Icon interface private int iconWidth; private int iconHeight; // Use to control processing private int currentIconIndex; private int cyclesCompleted; private boolean animationFinished = true; private Timer timer; /** * Create an AnimatedIcon that will continuously cycle with the * default (500ms). * * @param component the component the icon will be painted on * @param icons the Icons to be painted as part of the animation */ public AnimatedIcon(JComponent component, Icon... icons) { this(component, DEFAULT_DELAY, icons); } /** * Create an AnimatedIcon that will continuously cycle * * @param component the component the icon will be painted on * @param delay the delay between painting each icon, in milliseconds * @param icons the Icons to be painted as part of the animation */ public AnimatedIcon(JComponent component, int delay, Icon... icons) { this(component, delay, DEFAULT_CYCLES, icons); } /** * Create an AnimatedIcon specifying all the properties. * * @param component the component the icon will be painted on * @param delay the delay between painting each icon, in milliseconds * @param cycles the number of times to repeat the animation sequence * @param icons the Icons to be painted as part of the animation */ public AnimatedIcon( JComponent component, int delay, int cycles, Icon... icons) { this.component = component; setCycles( cycles ); for (int i = 0; i < icons.length; i++) { if (icons[i] == null) { String message = "Icon (" + i + ") cannot be null"; throw new IllegalArgumentException( message ); } else { addIcon( icons[i] ); } } timer = new Timer(delay, this); } /** * Add Icons to be used in the animation. * * @param icons the icons to be added */ public void addIcon(Icon... icons) { // if (icon == null) return; for (Icon icon : icons) { if (icon != null) { this.icons.add( icon ); calculateIconDimensions(); } } } /** * Calculate the width and height of the Icon based on the maximum * width and height of any individual Icon. */ private void calculateIconDimensions() { iconWidth = 0; iconHeight = 0; for (Icon icon : icons) { iconWidth = Math.max(iconWidth, icon.getIconWidth()); iconHeight = Math.max(iconHeight, icon.getIconHeight()); } } /** * Get the alignment of the Icon on the x-axis * * @return the alignment */ public float getAlignmentX() { return alignmentX; } /** * Specify the horizontal alignment of the icon. * * @param alignmentX common values are LEFT, CENTER (default) or RIGHT * although any value between 0.0 and 1.0 can be used */ public void setAlignmentX(float alignmentX) { this.alignmentX = alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f : alignmentX; } /** * Get the alignment of the icon on the y-axis * * @return the alignment */ public float getAlignmentY() { return alignmentY; } /** * Specify the vertical alignment of the Icon. * * @param alignmentY common values TOP, CENTER (default) or BOTTOM * although any value between 0.0 and 1.0 can be used */ public void setAlignmentY(float alignmentY) { this.alignmentY = alignmentY > 1.0f ? 1.0f : alignmentY < 0.0f ? 0.0f : alignmentY; } /** * Get the index of the currently visible Icon * * @return the index of the Icon */ public int getCurrentIconIndex() { return currentIconIndex; } /** * Set the index of the Icon to be displayed and then repaint the Icon. * * @param index the index of the Icon to be displayed */ public void setCurrentIconIndex(int index) { currentIconIndex = index; component.repaint(iconX, iconY, iconWidth, iconHeight); } /** * Get the cycles to complete before animation stops. * * @return the number of cycles */ public int getCycles() { return cycles; } /** * Specify the number of times to repeat each animation sequence, or cycle. * * @param cycles the number of cycles to complete before the animation * stops. The default is -1, which means the animation is * continuous. */ public void setCycles(int cycles) { this.cycles = cycles; } /** * Get the delay between painting each Icon * * @return the delay */ public int getDelay() { return timer.getDelay(); } /** * Specify the delay * * @param delay the delay between painting eachIcon (in milliseconds) */ public void setDelay(int delay) { timer.setDelay(delay); } /** * Get the Icon at the specified index. * * @param index the index of the Icon to be returned * @return the Icon at the specifed index * @exception IndexOutOfBoundsException if the index is out of range */ public Icon getIcon(int index) { return icons.get( index ); } /** * Get the number of Icons contained in this AnimatedIcon. * * @return the total number of Icons */ public int getIconCount() { return icons.size(); } /** * Get the showFirstIcon * * @return the showFirstIcon value */ public boolean isShowFirstIcon() { return showFirstIcon; } /** * Display the first icon when animation is finished. Otherwise the Icon * that was visible when the animation stopped will remain visible. * * @param showFirstIcon true when the first icon is to be displayed, * false otherwise */ public void setShowFirstIcon(boolean showFirstIcon) { this.showFirstIcon = showFirstIcon; } /** * Pause the animation. The animation can be restarted from the * current Icon using the restart() method. */ public void pause() { timer.stop(); } /** * Start the animation from the beginning. */ public void start() { if (!timer.isRunning()) { setCurrentIconIndex(0); animationFinished = false; cyclesCompleted = 0; timer.start(); } } /** * Restart the animation from where the animation was paused. Or, if the * animation has finished, it will be restarted from the beginning. */ public void restart() { if (!timer.isRunning()) { if (animationFinished) start(); else timer.restart(); } } /** * Stop the animation. The first icon will be redisplayed. */ public void stop() { timer.stop(); setCurrentIconIndex(0); animationFinished = true; } // // Implement the Icon Interface // /** * Gets the width of this icon. * * @return the width of the icon in pixels. */ // @Override public int getIconWidth() { return iconWidth; } /** * Gets the height of this icon. * * @return the height of the icon in pixels. */ // @Override public int getIconHeight() { return iconHeight; } /** * Paint the icons of this compound icon at the specified location * * @param c The component on which the icon is painted * @param g the graphics context * @param x the X coordinate of the icon's top-left corner * @param y the Y coordinate of the icon's top-left corner */ // @Override public void paintIcon(Component c, Graphics g, int x, int y) { // Saving the x, y coordinates allows us to only repaint the icon and // not the entire component for each animation if (c == component) { iconX = x; iconY = y; } // Determine the proper alignment of the Icon, then paint it Icon icon = icons.get( currentIconIndex ); int width = getIconWidth(); int height = getIconHeight(); int iconX = getOffset(width, icon.getIconWidth(), alignmentX); int iconY = getOffset(height, icon.getIconHeight(), alignmentY); icon.paintIcon(c, g, x + iconX, y + iconY); } /* * When the icon value is smaller than the maximum value of all icons the * icon needs to be aligned appropriately. Calculate the offset to be used * when painting the icon to achieve the proper alignment. */ private int getOffset(int maxValue, int iconValue, float alignment) { float offset = (maxValue - iconValue) * alignment; return Math.round(offset); } // // Implement the ActionListener interface // /** * Control the animation of the Icons when the Timer fires. */ public void actionPerformed(ActionEvent e) { // Display the next Icon in the animation sequence setCurrentIconIndex( getNextIconIndex(currentIconIndex, icons.size()) ); component.repaint(iconX, iconY, iconWidth, iconHeight); // Track the number of cycles that have been completed if (isCycleCompleted(currentIconIndex, icons.size())) { cyclesCompleted++; } // Stop the animation when the specified number of cycles is completed if (cycles > 0 && cycles <= cyclesCompleted) { timer.stop(); animationFinished = true; // Display the first Icon when required if (isShowFirstIcon() && getCurrentIconIndex() != 0) { new Thread(this).start(); } } } // // Implement the Runnable interface // public void run() { // Wait one more delay interval before displaying the first Icon try { Thread.sleep( timer.getDelay() ); setCurrentIconIndex(0); } catch(Exception e) {} } /** * Get the index of the next Icon to be displayed. * * This implementation displays the Icons in the order in which they were * added to this class. When the end is reached it will start back at the * first Icon. * * Typically this method, along with the isCycleCompleted() method, would * be extended to provide a custom animation sequence. * * @param currentIndex the index of the Icon currently displayed * @param iconCount the number of Icons to be displayed * @return the index of the next Icon to be displayed */ protected int getNextIconIndex(int currentIndex, int iconCount) { return ++currentIndex % iconCount; } /** * Check if the currently visible Icon is the last Icon to be displayed * in the animation sequence. If so, this indicates the completion of a * single cycle. The animation can continue for an unlimited number of * cycles or for a specified number of cycles. * * This implemention checks if the last icon is currently displayed. * * Typically this method, along with the getNextIconIndex() method, would * be extended to provide a custom animation sequence. * * @param currentIndex the index of the Icon currently displayed * @param iconCount the number of Icons to be displayed * @return the index of the next Icon to be displayed */ protected boolean isCycleCompleted(int currentIndex, int iconCount) { return currentIndex == iconCount - 1; } }