Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright (C) 2010 The Android Open Source Project
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.actionbarsherlock.internal.nineoldandroids.animation;
  
  import java.util.HashMap;
  import java.util.List;
  
This class plays a set of Animator objects in the specified order. Animations can be set up to play together, in sequence, or after a specified delay.

There are two different approaches to adding animations to a AnimatorSet: either the playTogether() or playSequentially() methods can be called to add a set of animations all at once, or the play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) can be used in conjunction with methods in the Builder class to add animations one by one.

It is possible to set up a AnimatorSet with circular dependencies between its animations. For example, an animation a1 could be set up to start before animation a2, a2 before a3, and a3 before a1. The results of this configuration are undefined, but will typically result in none of the affected animations being played. Because of this (and because circular dependencies do not make logical sense anyway), circular dependencies should be avoided, and the dependency flow of animations should only be in one direction.

  
  @SuppressWarnings("unchecked")
  public final class AnimatorSet extends Animator {

    
Internal variables NOTE: This object implements the clone() method, making a deep copy of any referenced objects. As other non-trivial fields are added to this class, make sure to add logic to clone() to make deep copies of them.
  

    
Tracks animations currently being played, so that we know what to cancel or end when cancel() or end() is called on this AnimatorSet
  
      private ArrayList<AnimatormPlayingSet = new ArrayList<Animator>();

    
Contains all nodes, mapped to their respective Animators. When new dependency information is added for an Animator, we want to add it to a single node representing that Animator, not create a new Node if one already exists.
  
      private HashMap<AnimatorNodemNodeMap = new HashMap<AnimatorNode>();

    
Set of all nodes created for this AnimatorSet. This list is used upon starting the set, and the nodes are placed in sorted order into the sortedNodes collection.
  
      private ArrayList<NodemNodes = new ArrayList<Node>();

    
The sorted list of nodes. This is the order in which the animations will be played. The details about when exactly they will be played depend on the dependency relationships of the nodes.
  
      private ArrayList<NodemSortedNodes = new ArrayList<Node>();

    
Flag indicating whether the nodes should be sorted prior to playing. This flag allows us to cache the previous sorted nodes so that if the sequence is replayed with no changes, it does not have to re-sort the nodes again.
  
      private boolean mNeedsSort = true;
  
      private AnimatorSetListener mSetListener = null;

    
Flag indicating that the AnimatorSet has been manually terminated (by calling cancel() or end()). This flag is used to avoid starting other animations when currently-playing child animations of this AnimatorSet end. It also determines whether cancel/end notifications are sent out via the normal AnimatorSetListener mechanism.
  
      boolean mTerminated = false;

    
Indicates whether an AnimatorSet has been start()'d, whether or not there is a nonzero startDelay.
 
     private boolean mStarted = false;
 
     // The amount of time in ms to delay starting the animation after start() is called
     private long mStartDelay = 0;
 
     // Animator used for a nonzero startDelay
     private ValueAnimator mDelayAnim = null;
 
 
     // How long the child animations should last in ms. The default value is negative, which
     // simply means that there is no duration set on the AnimatorSet. When a real duration is
     // set, it is passed along to the child animations.
     private long mDuration = -1;


    
Sets up this AnimatorSet to play all of the supplied animations at the same time.

Parameters:
items The animations that will be started simultaneously.
 
     public void playTogether(Animator... items) {
         if (items != null) {
              = true;
             Builder builder = play(items[0]);
             for (int i = 1; i < items.length; ++i) {
                 builder.with(items[i]);
             }
         }
     }

    
Sets up this AnimatorSet to play all of the supplied animations at the same time.

Parameters:
items The animations that will be started simultaneously.
 
     public void playTogether(Collection<Animatoritems) {
         if (items != null && items.size() > 0) {
              = true;
             Builder builder = null;
             for (Animator anim : items) {
                 if (builder == null) {
                     builder = play(anim);
                 } else {
                     builder.with(anim);
                 }
             }
         }
     }

    
Sets up this AnimatorSet to play each of the supplied animations when the previous animation ends.

Parameters:
items The animations that will be started one after another.
 
     public void playSequentially(Animator... items) {
         if (items != null) {
              = true;
             if (items.length == 1) {
                 play(items[0]);
             } else {
                 for (int i = 0; i < items.length - 1; ++i) {
                     play(items[i]).before(items[i+1]);
                 }
             }
         }
     }

    
Sets up this AnimatorSet to play each of the supplied animations when the previous animation ends.

Parameters:
items The animations that will be started one after another.
 
     public void playSequentially(List<Animatoritems) {
         if (items != null && items.size() > 0) {
              = true;
             if (items.size() == 1) {
                 play(items.get(0));
             } else {
                 for (int i = 0; i < items.size() - 1; ++i) {
                     play(items.get(i)).before(items.get(i+1));
                 }
             }
         }
     }

    
Returns the current list of child Animator objects controlled by this AnimatorSet. This is a copy of the internal list; modifications to the returned list will not affect the AnimatorSet, although changes to the underlying Animator objects will affect those objects being managed by the AnimatorSet.

Returns:
ArrayList<Animator> The list of child animations of this AnimatorSet.
 
     public ArrayList<AnimatorgetChildAnimations() {
         ArrayList<AnimatorchildList = new ArrayList<Animator>();
         for (Node node : ) {
             childList.add(node.animation);
         }
         return childList;
     }

    
Sets the target object for all current child animations of this AnimatorSet that take targets (com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator and AnimatorSet).

Parameters:
target The object being animated
 
     @Override
     public void setTarget(Object target) {
         for (Node node : ) {
             Animator animation = node.animation;
             if (animation instanceof AnimatorSet) {
                 ((AnimatorSet)animation).setTarget(target);
             } else if (animation instanceof ObjectAnimator) {
                 ((ObjectAnimator)animation).setTarget(target);
             }
         }
     }

    
Sets the TimeInterpolator for all current child animations of this AnimatorSet.

Parameters:
interpolator the interpolator to be used by each child animation of this AnimatorSet
 
     @Override
     public void setInterpolator(/*Time*/Interpolator interpolator) {
         for (Node node : ) {
             node.animation.setInterpolator(interpolator);
         }
     }

    
This method creates a Builder object, which is used to set up playing constraints. This initial play() method tells the Builder the animation that is the dependency for the succeeding commands to the Builder. For example, calling play(a1).with(a2) sets up the AnimatorSet to play a1 and a2 at the same time, play(a1).before(a2) sets up the AnimatorSet to play a1 first, followed by a2, and play(a1).after(a2) sets up the AnimatorSet to play a2 first, followed by a1.

Note that play() is the only way to tell the Builder the animation upon which the dependency is created, so successive calls to the various functions in Builder will all refer to the initial parameter supplied in play() as the dependency of the other animations. For example, calling play(a1).before(a2).before(a3) will play both a2 and a3 when a1 ends; it does not set up a dependency between a2 and a3.

Parameters:
anim The animation that is the dependency used in later calls to the methods in the returned Builder object. A null parameter will result in a null Builder return value.
Returns:
Builder The object that constructs the AnimatorSet based on the dependencies outlined in the calls to play and the other methods in the Builder
 
     public Builder play(Animator anim) {
         if (anim != null) {
              = true;
             return new Builder(anim);
         }
         return null;
     }

    

Note that canceling a AnimatorSet also cancels all of the animations that it is responsible for.

 
     @Override
     public void cancel() {
          = true;
         if (isStarted()) {
             ArrayList<AnimatorListenertmpListeners = null;
             if ( != null) {
                 tmpListeners = (ArrayList<AnimatorListener>) .clone();
                 for (AnimatorListener listener : tmpListeners) {
                     listener.onAnimationCancel(this);
                 }
             }
             if ( != null && .isRunning()) {
                 // If we're currently in the startDelay period, just cancel that animator and
                 // send out the end event to all listeners
                 .cancel();
             } else  if (.size() > 0) {
                 for (Node node : ) {
                     node.animation.cancel();
                 }
             }
             if (tmpListeners != null) {
                 for (AnimatorListener listener : tmpListeners) {
                     listener.onAnimationEnd(this);
                 }
             }
              = false;
         }
     }

    

Note that ending a AnimatorSet also ends all of the animations that it is responsible for.

 
     @Override
     public void end() {
          = true;
         if (isStarted()) {
             if (.size() != .size()) {
                 // hasn't been started yet - sort the nodes now, then end them
                 sortNodes();
                 for (Node node : ) {
                     if ( == null) {
                          = new AnimatorSetListener(this);
                     }
                     node.animation.addListener();
                 }
             }
             if ( != null) {
                 .cancel();
             }
             if (.size() > 0) {
                 for (Node node : ) {
                     node.animation.end();
                 }
             }
             if ( != null) {
                 ArrayList<AnimatorListenertmpListeners =
                         (ArrayList<AnimatorListener>) .clone();
                 for (AnimatorListener listener : tmpListeners) {
                     listener.onAnimationEnd(this);
                 }
             }
              = false;
         }
     }

    
Returns true if any of the child animations of this AnimatorSet have been started and have not yet ended.

Returns:
Whether this AnimatorSet has been started and has not yet ended.
 
     @Override
     public boolean isRunning() {
         for (Node node : ) {
             if (node.animation.isRunning()) {
                 return true;
             }
         }
         return false;
     }
 
     @Override
     public boolean isStarted() {
         return ;
     }

    
The amount of time, in milliseconds, to delay starting the animation after start() is called.

Returns:
the number of milliseconds to delay running the animation
 
     @Override
     public long getStartDelay() {
         return ;
     }

    
The amount of time, in milliseconds, to delay starting the animation after start() is called.

Parameters:
startDelay The amount of the delay, in milliseconds
 
     @Override
     public void setStartDelay(long startDelay) {
          = startDelay;
     }

    
Gets the length of each of the child animations of this AnimatorSet. This value may be less than 0, which indicates that no duration has been set on this AnimatorSet and each of the child animations will use their own duration.

Returns:
The length of the animation, in milliseconds, of each of the child animations of this AnimatorSet.
 
     @Override
     public long getDuration() {
         return ;
     }

    
Sets the length of each of the current child animations of this AnimatorSet. By default, each child animation will use its own duration. If the duration is set on the AnimatorSet, then each child animation inherits this duration.

Parameters:
duration The length of the animation, in milliseconds, of each of the child animations of this AnimatorSet.
 
     @Override
     public AnimatorSet setDuration(long duration) {
         if (duration < 0) {
             throw new IllegalArgumentException("duration must be a value of zero or greater");
         }
         for (Node node : ) {
             // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
             // insert "play-after" delays
             node.animation.setDuration(duration);
         }
          = duration;
         return this;
     }
 
     @Override
     public void setupStartValues() {
         for (Node node : ) {
             node.animation.setupStartValues();
         }
     }
 
     @Override
     public void setupEndValues() {
         for (Node node : ) {
             node.animation.setupEndValues();
         }
     }

    

Starting this AnimatorSet will, in turn, start the animations for which it is responsible. The details of when exactly those animations are started depends on the dependency relationships that have been set up between the animations.

 
     @Override
     public void start() {
          = false;
          = true;
 
         // First, sort the nodes (if necessary). This will ensure that sortedNodes
         // contains the animation nodes in the correct order.
         sortNodes();
 
         int numSortedNodes = .size();
         for (int i = 0; i < numSortedNodes; ++i) {
             Node node = .get(i);
             // First, clear out the old listeners
             ArrayList<AnimatorListeneroldListeners = node.animation.getListeners();
             if (oldListeners != null && oldListeners.size() > 0) {
                 final ArrayList<AnimatorListenerclonedListeners = new
                         ArrayList<AnimatorListener>(oldListeners);
 
                 for (AnimatorListener listener : clonedListeners) {
                     if (listener instanceof DependencyListener ||
                             listener instanceof AnimatorSetListener) {
                         node.animation.removeListener(listener);
                     }
                 }
             }
         }
 
         // nodesToStart holds the list of nodes to be started immediately. We don't want to
         // start the animations in the loop directly because we first need to set up
         // dependencies on all of the nodes. For example, we don't want to start an animation
         // when some other animation also wants to start when the first animation begins.
         final ArrayList<NodenodesToStart = new ArrayList<Node>();
         for (int i = 0; i < numSortedNodes; ++i) {
             Node node = .get(i);
             if ( == null) {
                  = new AnimatorSetListener(this);
             }
             if (node.dependencies == null || node.dependencies.size() == 0) {
                 nodesToStart.add(node);
             } else {
                 int numDependencies = node.dependencies.size();
                 for (int j = 0; j < numDependencies; ++j) {
                     Dependency dependency = node.dependencies.get(j);
                     dependency.node.animation.addListener(
                             new DependencyListener(thisnodedependency.rule));
                 }
                 node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
             }
             node.animation.addListener();
         }
         // Now that all dependencies are set up, start the animations that should be started.
         if ( <= 0) {
             for (Node node : nodesToStart) {
                 node.animation.start();
                 .add(node.animation);
             }
         } else {
              = ValueAnimator.ofFloat(0f, 1f);
             .setDuration();
             .addListener(new AnimatorListenerAdapter() {
                 boolean canceled = false;
                 public void onAnimationCancel(Animator anim) {
                      = true;
                 }
                 public void onAnimationEnd(Animator anim) {
                     if (!) {
                         int numNodes = nodesToStart.size();
                         for (int i = 0; i < numNodes; ++i) {
                             Node node = nodesToStart.get(i);
                             node.animation.start();
                             .add(node.animation);
                         }
                     }
                 }
             });
             .start();
         }
         if ( != null) {
             ArrayList<AnimatorListenertmpListeners =
                     (ArrayList<AnimatorListener>) .clone();
             int numListeners = tmpListeners.size();
             for (int i = 0; i < numListeners; ++i) {
                 tmpListeners.get(i).onAnimationStart(this);
             }
         }
         if (.size() == 0 &&  == 0) {
             // Handle unusual case where empty AnimatorSet is started - should send out
             // end event immediately since the event will not be sent out at all otherwise
              = false;
             if ( != null) {
                 ArrayList<AnimatorListenertmpListeners =
                         (ArrayList<AnimatorListener>) .clone();
                 int numListeners = tmpListeners.size();
                 for (int i = 0; i < numListeners; ++i) {
                     tmpListeners.get(i).onAnimationEnd(this);
                 }
             }
         }
     }
 
     @Override
     public AnimatorSet clone() {
         final AnimatorSet anim = (AnimatorSetsuper.clone();
         /*
          * The basic clone() operation copies all items. This doesn't work very well for
          * AnimatorSet, because it will copy references that need to be recreated and state
          * that may not apply. What we need to do now is put the clone in an uninitialized
          * state, with fresh, empty data structures. Then we will build up the nodes list
          * manually, as we clone each Node (and its animation). The clone will then be sorted,
          * and will populate any appropriate lists, when it is started.
          */
         anim.mNeedsSort = true;
         anim.mTerminated = false;
         anim.mStarted = false;
         anim.mPlayingSet = new ArrayList<Animator>();
         anim.mNodeMap = new HashMap<AnimatorNode>();
         anim.mNodes = new ArrayList<Node>();
         anim.mSortedNodes = new ArrayList<Node>();
 
         // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
         // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
         // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
         HashMap<NodeNodenodeCloneMap = new HashMap<NodeNode>(); // <old, new>
         for (Node node : ) {
             Node nodeClone = node.clone();
             nodeCloneMap.put(nodenodeClone);
             anim.mNodes.add(nodeClone);
             anim.mNodeMap.put(nodeClone.animationnodeClone);
             // Clear out the dependencies in the clone; we'll set these up manually later
             nodeClone.dependencies = null;
             nodeClone.tmpDependencies = null;
             nodeClone.nodeDependents = null;
             nodeClone.nodeDependencies = null;
             // clear out any listeners that were set up by the AnimatorSet; these will
             // be set up when the clone's nodes are sorted
             ArrayList<AnimatorListenercloneListeners = nodeClone.animation.getListeners();
             if (cloneListeners != null) {
                 ArrayList<AnimatorListenerlistenersToRemove = null;
                 for (AnimatorListener listener : cloneListeners) {
                     if (listener instanceof AnimatorSetListener) {
                         if (listenersToRemove == null) {
                             listenersToRemove = new ArrayList<AnimatorListener>();
                         }
                         listenersToRemove.add(listener);
                     }
                 }
                 if (listenersToRemove != null) {
                     for (AnimatorListener listener : listenersToRemove) {
                         cloneListeners.remove(listener);
                     }
                 }
             }
         }
         // Now that we've cloned all of the nodes, we're ready to walk through their
         // dependencies, mapping the old dependencies to the new nodes
         for (Node node : ) {
             Node nodeClone = nodeCloneMap.get(node);
             if (node.dependencies != null) {
                 for (Dependency dependency : node.dependencies) {
                     Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
                     Dependency cloneDependency = new Dependency(clonedDependencyNode,
                             dependency.rule);
                     nodeClone.addDependency(cloneDependency);
                 }
             }
         }
 
         return anim;
     }

    
This class is the mechanism by which animations are started based on events in other animations. If an animation has multiple dependencies on other animations, then all dependencies must be satisfied before the animation is started.
 
     private static class DependencyListener implements AnimatorListener {
 
         private AnimatorSet mAnimatorSet;
 
         // The node upon which the dependency is based.
         private Node mNode;
 
         // The Dependency rule (WITH or AFTER) that the listener should wait for on
         // the node
         private int mRule;
 
         public DependencyListener(AnimatorSet animatorSetNode nodeint rule) {
             this. = animatorSet;
             this. = node;
             this. = rule;
         }

        
Ignore cancel events for now. We may want to handle this eventually, to prevent follow-on animations from running when some dependency animation is canceled.
 
         public void onAnimationCancel(Animator animation) {
         }

        
An end event is received - see if this is an event we are listening for
 
         public void onAnimationEnd(Animator animation) {
             if ( == .) {
                 startIfReady(animation);
             }
         }

        
Ignore repeat events for now
 
         public void onAnimationRepeat(Animator animation) {
         }

        
A start event is received - see if this is an event we are listening for
 
         public void onAnimationStart(Animator animation) {
             if ( == .) {
                 startIfReady(animation);
             }
         }

        
Check whether the event received is one that the node was waiting for. If so, mark it as complete and see whether it's time to start the animation.

Parameters:
dependencyAnimation the animation that sent the event.
 
         private void startIfReady(Animator dependencyAnimation) {
             if (.) {
                 // if the parent AnimatorSet was canceled, then don't start any dependent anims
                 return;
             }
             Dependency dependencyToRemove = null;
             int numDependencies = ..size();
             for (int i = 0; i < numDependencies; ++i) {
                 Dependency dependency = ..get(i);
                 if (dependency.rule ==  &&
                         dependency.node.animation == dependencyAnimation) {
                     // rule fired - remove the dependency and listener and check to
                     // see whether it's time to start the animation
                     dependencyToRemove = dependency;
                     dependencyAnimation.removeListener(this);
                     break;
                 }
             }
             ..remove(dependencyToRemove);
             if (..size() == 0) {
                 // all dependencies satisfied: start the animation
                 ..start();
                 ..add(.);
             }
         }
 
     }
 
     private class AnimatorSetListener implements AnimatorListener {
 
         private AnimatorSet mAnimatorSet;
 
         AnimatorSetListener(AnimatorSet animatorSet) {
              = animatorSet;
         }
 
         public void onAnimationCancel(Animator animation) {
             if (!) {
                 // Listeners are already notified of the AnimatorSet canceling in cancel().
                 // The logic below only kicks in when animations end normally
                 if (.size() == 0) {
                     if ( != null) {
                         int numListeners = .size();
                         for (int i = 0; i < numListeners; ++i) {
                             .get(i).onAnimationCancel();
                         }
                     }
                 }
             }
         }
 
         public void onAnimationEnd(Animator animation) {
             animation.removeListener(this);
             .remove(animation);
             Node animNode = ..get(animation);
             animNode.done = true;
             if (!) {
                 // Listeners are already notified of the AnimatorSet ending in cancel() or
                 // end(); the logic below only kicks in when animations end normally
                 ArrayList<NodesortedNodes = .;
                 boolean allDone = true;
                 int numSortedNodes = sortedNodes.size();
                 for (int i = 0; i < numSortedNodes; ++i) {
                     if (!sortedNodes.get(i).) {
                         allDone = false;
                         break;
                     }
                 }
                 if (allDone) {
                     // If this was the last child animation to end, then notify listeners that this
                     // AnimatorSet has ended
                     if ( != null) {
                         ArrayList<AnimatorListenertmpListeners =
                                 (ArrayList<AnimatorListener>) .clone();
                         int numListeners = tmpListeners.size();
                         for (int i = 0; i < numListeners; ++i) {
                             tmpListeners.get(i).onAnimationEnd();
                         }
                     }
                     . = false;
                 }
             }
         }
 
         // Nothing to do
         public void onAnimationRepeat(Animator animation) {
         }
 
         // Nothing to do
         public void onAnimationStart(Animator animation) {
         }
 
     }

    
This method sorts the current set of nodes, if needed. The sort is a simple DependencyGraph sort, which goes like this: - All nodes without dependencies become 'roots' - while roots list is not null - for each root r - add r to sorted list - remove r as a dependency from any other node - any nodes with no dependencies are added to the roots list
 
     private void sortNodes() {
         if () {
             .clear();
             ArrayList<Noderoots = new ArrayList<Node>();
             int numNodes = .size();
             for (int i = 0; i < numNodes; ++i) {
                 Node node = .get(i);
                 if (node.dependencies == null || node.dependencies.size() == 0) {
                     roots.add(node);
                 }
             }
             ArrayList<NodetmpRoots = new ArrayList<Node>();
             while (roots.size() > 0) {
                 int numRoots = roots.size();
                 for (int i = 0; i < numRoots; ++i) {
                     Node root = roots.get(i);
                     .add(root);
                     if (root.nodeDependents != null) {
                         int numDependents = root.nodeDependents.size();
                         for (int j = 0; j < numDependents; ++j) {
                             Node node = root.nodeDependents.get(j);
                             node.nodeDependencies.remove(root);
                             if (node.nodeDependencies.size() == 0) {
                                 tmpRoots.add(node);
                             }
                         }
                     }
                 }
                 roots.clear();
                 roots.addAll(tmpRoots);
                 tmpRoots.clear();
             }
              = false;
             if (.size() != .size()) {
                 throw new IllegalStateException("Circular dependencies cannot exist"
                         + " in AnimatorSet");
             }
         } else {
             // Doesn't need sorting, but still need to add in the nodeDependencies list
             // because these get removed as the event listeners fire and the dependencies
             // are satisfied
             int numNodes = .size();
             for (int i = 0; i < numNodes; ++i) {
                 Node node = .get(i);
                 if (node.dependencies != null && node.dependencies.size() > 0) {
                     int numDependencies = node.dependencies.size();
                     for (int j = 0; j < numDependencies; ++j) {
                         Dependency dependency = node.dependencies.get(j);
                         if (node.nodeDependencies == null) {
                             node.nodeDependencies = new ArrayList<Node>();
                         }
                         if (!node.nodeDependencies.contains(dependency.node)) {
                             node.nodeDependencies.add(dependency.node);
                         }
                     }
                 }
                 // nodes are 'done' by default; they become un-done when started, and done
                 // again when ended
                 node.done = false;
             }
         }
     }

    
Dependency holds information about the node that some other node is dependent upon and the nature of that dependency.
 
     private static class Dependency {
         static final int WITH = 0; // dependent node must start with this dependency node
         static final int AFTER = 1; // dependent node must start when this dependency node finishes
 
         // The node that the other node with this Dependency is dependent upon
         public Node node;
 
         // The nature of the dependency (WITH or AFTER)
         public int rule;
 
         public Dependency(Node nodeint rule) {
             this. = node;
             this. = rule;
         }
     }

    
A Node is an embodiment of both the Animator that it wraps as well as any dependencies that are associated with that Animation. This includes both dependencies upon other nodes (in the dependencies list) as well as dependencies of other nodes upon this (in the nodeDependents list).
 
     private static class Node implements Cloneable {
         public Animator animation;

        
These are the dependencies that this node's animation has on other nodes. For example, if this node's animation should begin with some other animation ends, then there will be an item in this node's dependencies list for that other animation's node.
 
         public ArrayList<Dependencydependencies = null;

        
tmpDependencies is a runtime detail. We use the dependencies list for sorting. But we also use the list to keep track of when multiple dependencies are satisfied, but removing each dependency as it is satisfied. We do not want to remove the dependency itself from the list, because we need to retain that information if the AnimatorSet is launched in the future. So we create a copy of the dependency list when the AnimatorSet starts and use this tmpDependencies list to track the list of satisfied dependencies.
 
         public ArrayList<DependencytmpDependencies = null;

        
nodeDependencies is just a list of the nodes that this Node is dependent upon. This information is used in sortNodes(), to determine when a node is a root.
 
         public ArrayList<NodenodeDependencies = null;

        
nodeDepdendents is the list of nodes that have this node as a dependency. This is a utility field used in sortNodes to facilitate removing this node as a dependency when it is a root node.
 
         public ArrayList<NodenodeDependents = null;

        
Flag indicating whether the animation in this node is finished. This flag is used by AnimatorSet to check, as each animation ends, whether all child animations are done and it's time to send out an end event for the entire AnimatorSet.
 
         public boolean done = false;

        
Constructs the Node with the animation that it encapsulates. A Node has no dependencies by default; dependencies are added via the addDependency() method.

Parameters:
animation The animation that the Node encapsulates.
 
         public Node(Animator animation) {
             this. = animation;
         }

        
Add a dependency to this Node. The dependency includes information about the node that this node is dependency upon and the nature of the dependency.

Parameters:
dependency
 
         public void addDependency(Dependency dependency) {
             if ( == null) {
                  = new ArrayList<Dependency>();
                  = new ArrayList<Node>();
             }
             .add(dependency);
             if (!.contains(dependency.node)) {
                 .add(dependency.node);
             }
             Node dependencyNode = dependency.node;
             if (dependencyNode.nodeDependents == null) {
                 dependencyNode.nodeDependents = new ArrayList<Node>();
             }
             dependencyNode.nodeDependents.add(this);
         }
 
         @Override
         public Node clone() {
             try {
                 Node node = (Nodesuper.clone();
                 node.animation = .clone();
                 return node;
             } catch (CloneNotSupportedException e) {
                throw new AssertionError();
             }
         }
     }

    
The Builder object is a utility class to facilitate adding animations to a AnimatorSet along with the relationships between the various animations. The intention of the Builder methods, along with the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) method of AnimatorSet is to make it possible to express the dependency relationships of animations in a natural way. Developers can also use the playTogether() and AnimatorSet.playSequentially(com.actionbarsherlock.internal.nineoldandroids.animation.Animator[]) methods if these suit the need, but it might be easier in some situations to express the AnimatorSet of animations in pairs.

The Builder object cannot be constructed directly, but is rather constructed internally via a call to AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator).

For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to play when anim2 finishes, and anim4 to play when anim3 finishes:

     AnimatorSet s = new AnimatorSet();
     s.play(anim1).with(anim2);
     s.play(anim2).before(anim3);
     s.play(anim4).after(anim3);
 

Note in the example that both before(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) and after(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) are used. These are just different ways of expressing the same relationship and are provided to make it easier to say things in a way that is more natural, depending on the situation.

It is possible to make several calls into the same Builder object to express multiple relationships. However, note that it is only the animation passed into the initial AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) method that is the dependency in any of the successive calls to the Builder object. For example, the following code starts both anim2 and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and anim3:

   AnimatorSet s = new AnimatorSet();
   s.play(anim1).before(anim2).before(anim3);
 
If the desired result is to play anim1 then anim2 then anim3, this code expresses the relationship correctly:
   AnimatorSet s = new AnimatorSet();
   s.play(anim1).before(anim2);
   s.play(anim2).before(anim3);
 

Note that it is possible to express relationships that cannot be resolved and will not result in sensible results. For example, play(anim1).after(anim1) makes no sense. In general, circular dependencies like this one (or more indirect ones where a depends on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets that can boil down to a simple, one-way relationship of animations starting with, before, and after other, different, animations.

    public class Builder {

        
This tracks the current node being processed. It is supplied to the play() method of AnimatorSet and passed into the constructor of Builder.
        private Node mCurrentNode;

        
package-private constructor. Builders are only constructed by AnimatorSet, when the play() method is called.

Parameters:
anim The animation that is the dependency for the other animations passed into the other methods of this Builder object.
        Builder(Animator anim) {
             = .get(anim);
            if ( == null) {
                 = new Node(anim);
                .put(anim);
                .add();
            }
        }

        
Sets up the given animation to play at the same time as the animation supplied in the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) call that created this Builder object.

Parameters:
anim The animation that will play when the animation supplied to the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) method starts.
        public Builder with(Animator anim) {
            Node node = .get(anim);
            if (node == null) {
                node = new Node(anim);
                .put(animnode);
                .add(node);
            }
            Dependency dependency = new Dependency(.);
            node.addDependency(dependency);
            return this;
        }

        
Sets up the given animation to play when the animation supplied in the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) call that created this Builder object ends.

Parameters:
anim The animation that will play when the animation supplied to the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) method ends.
        public Builder before(Animator anim) {
            Node node = .get(anim);
            if (node == null) {
                node = new Node(anim);
                .put(animnode);
                .add(node);
            }
            Dependency dependency = new Dependency(.);
            node.addDependency(dependency);
            return this;
        }

        
Sets up the given animation to play when the animation supplied in the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) call that created this Builder object to start when the animation supplied in this method call ends.

Parameters:
anim The animation whose end will cause the animation supplied to the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) method to play.
        public Builder after(Animator anim) {
            Node node = .get(anim);
            if (node == null) {
                node = new Node(anim);
                .put(animnode);
                .add(node);
            }
            Dependency dependency = new Dependency(node.);
            .addDependency(dependency);
            return this;
        }

        
Sets up the animation supplied in the AnimatorSet.play(com.actionbarsherlock.internal.nineoldandroids.animation.Animator) call that created this Builder object to play when the given amount of time elapses.

Parameters:
delay The number of milliseconds that should elapse before the animation starts.
        public Builder after(long delay) {
            // setup dummy ValueAnimator just to run the clock
            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
            anim.setDuration(delay);
            after(anim);
            return this;
        }
    }
New to GrepCode? Check out our FAQ X