/** * 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 (mNeedsSort) { mSortedNodes.clear(); ArrayList<Node> roots = new ArrayList<Node>(); int numNodes = mNodes.size(); for (int i = 0; i < numNodes; ++i) { Node node = mNodes.get(i); if (node.dependencies == null || node.dependencies.size() == 0) { roots.add(node); } } ArrayList<Node> tmpRoots = new ArrayList<Node>(); while (roots.size() > 0) { int numRoots = roots.size(); for (int i = 0; i < numRoots; ++i) { Node root = roots.get(i); mSortedNodes.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(); } mNeedsSort = false; if (mSortedNodes.size() != mNodes.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 = mNodes.size(); for (int i = 0; i < numNodes; ++i) { Node node = mNodes.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; } } }
@Override public AnimatorSet clone() { final AnimatorSet anim = (AnimatorSet) super.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<Animator, Node>(); 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<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new> for (Node node : mNodes) { Node nodeClone = node.clone(); nodeCloneMap.put(node, nodeClone); anim.mNodes.add(nodeClone); anim.mNodeMap.put(nodeClone.animation, nodeClone); // 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<AnimatorListener> cloneListeners = nodeClone.animation.getListeners(); if (cloneListeners != null) { ArrayList<AnimatorListener> listenersToRemove = 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 : mNodes) { 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; }