Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android屬性動畫AnimatorSet源碼簡單分析

Android屬性動畫AnimatorSet源碼簡單分析

編輯:關於Android編程

跟上之前的文章
Android屬性動畫ValueAnimator源碼簡單分析
繼續看AnimatorSet源碼的大概過程。

AnimatorSet 提供了一種把多個動畫放到一起,按照某種特定的順序來播放,比如一個接一個的播放或者多個動畫一起播放。

AnimatorSet簡單使用隨便舉一個最簡單的例子

        //AnimatorSet
        AnimatorSet animSet = new AnimatorSet();
        ObjectAnimator objectAlpha = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f, 1f);
        ObjectAnimator objectScaleX = ObjectAnimator.ofFloat(this, "scaleX", 1f, 0, 1f);
        ObjectAnimator objectScaleY = ObjectAnimator.ofFloat(this, "scaleY", 1f, 0, 1f);
        animSet.setDuration(3000);
        animSet.setInterpolator(new LinearInterpolator());
//      animSet.playSequentially(objectAlpha, objectScaleX, objectScaleY);
        animSet.playTogether(objectAlpha, objectScaleX, objectScaleY);
        animSet.start();

還是按照使用的規則開始分析AnimatorSet。對應AnimatorSet的源碼准備從三個方面來分析。
1. AnimatorSet.playSequentially() 函數裡面干了些啥。(動畫順序播放)
2. AnimatorSet.playTogether() 函數裡面干了些啥。(動畫同時播放)
3. AnimatorSet.start() 函數裡面干了些啥。

一,AnimatorSet.playSequentially() 函數裡面干了些啥

如果調用了這個函數代表動畫按照順序播放,我們帶著好奇跟到源碼裡面去看看,到底是怎麼讓他順序播放的。
AnimatorSet類的playSequentially 函數。

    public void playSequentially(Animator... items) {
        if (items != null) {
            mNeedsSort = true;
            if (items.length == 1) {
                play(items[0]);
            } else {
                mReversible = false;
                for (int i = 0; i < items.length - 1; ++i) {
                    play(items[i]).before(items[i+1]);
                }
            }
        }
    }

參數的長度是可變的,參數是具體的動畫。
4-6行,如果只是傳入了一個動畫,直接調用了play函數,參數就是這個動畫。那就得看play函數了。

    public Builder play(Animator anim) {
        if (anim != null) {
            mNeedsSort = true;
            return new Builder(anim);
        }
        return null;
    }

繼續Builder的構造函數。AnimaterSet的內部類Builder類

        Builder(Animator anim) {
            mCurrentNode = mNodeMap.get(anim);
            if (mCurrentNode == null) {
                mCurrentNode = new Node(anim);
                mNodeMap.put(anim, mCurrentNode);
                mNodes.add(mCurrentNode);
            }
        }

new了一個Node,記錄了Builder 的當前節點mCurrentNode,並且把這個節點加入到了mNodeMap中(key是動畫,value是new出來的節點)。同時把這個節點加入到了mNodes裡面。這裡要注意mCurrentNode 是屬於Builder類的,mNodeMap和mNodes是屬於AnimatorSet類的。
繼續AnimatorSet類的playSequentially 函數。6-11行,當傳入的動畫的個數大於1的時候。循環調用 play(items[i]).before(items[i+1]); play函數上面分析了,由於這裡是for循環我們只分析一次的就好了,我們剛才看了play函數返回的是一個Build對象注意這裡我們記錄了mCurrentNode,然後繼續調用這個Builder對象的before函數,跟蹤進去。

        public Builder before(Animator anim) {
            mReversible = false;
            Node node = mNodeMap.get(anim);
            if (node == null) {
                node = new Node(anim);
                mNodeMap.put(anim, node);
                mNodes.add(node);
            }
            Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
            node.addDependency(dependency);
            return this;
        }

2-8行,這個應該不用解釋,就是new一個Node同樣把這個Node加入到AnimatorSet的mNodeMap和mNodes裡面去。
9行 生成了一個Dependency對象,注意這裡傳入的是mCurrentNode是剛才play函數裡面生成的,並且模式是Dependency.AFTER的模式(順序播放的模式)。
10行 addDepenDency 跟蹤進去看看吧

        public void addDependency(Dependency dependency) {
            if (dependencies == null) {
                dependencies = new ArrayList();
                nodeDependencies = new ArrayList();
            }
            dependencies.add(dependency);
            if (!nodeDependencies.contains(dependency.node)) {
                nodeDependencies.add(dependency.node);
            }
            Node dependencyNode = dependency.node;
            if (dependencyNode.nodeDependents == null) {
                dependencyNode.nodeDependents = new ArrayList();
            }
            dependencyNode.nodeDependents.add(this);
        }

這裡要時刻記住dependency裡面放的動畫是前一個的動畫節點(play函數裡面賦值的mCurrentNode)。注意Node的nodeDependents和nodeDependencies兩個List。這裡可能不是很好講明白直接舉個例子吧。例子是這樣的play參數假設是A動畫,befor參數假設是B動畫。那麼play(A).before(B)調用完之後,看下Node的對應關系應該是這樣的。這個關系在下面分析play的時候有非常非常的重要。
play函數的時候:
A對應的Node(這個時候還沒有對應關系)
before函數的時候:
B對應的Node ->nodeDependencies 裡面加了A對應的Node(對應函數的2-6行)
A對應的Node ->nodeDependents 裡面加了B對於的Node(對應函數的10-14行)

總結AnimatorSet.playSequentially()做的事情,比如我們AnimatorSet.playSequentially()傳入三個動畫A,B,C。調用完playSequentially函數之後的狀態是怎樣的呢。
AnimatorSet裡面的主要變量的變化:
mNodeMap加入了三個元素(A,A對應的Node), (B,B對應的Node), (C,C對應的Node)。mNodes裡面加入了三個元素A,B,C。
各個動畫對應的Node裡面元素的變化:playSequentially裡面會循環兩次
第一次循環
A對應的Node(沒有對應關系)
B對應的Node ->nodeDependencies 裡面加了A對應的Node
A對應的Node ->nodeDependents 裡面加了B對於的Node
第二次循環
C對應的Node->nodeDependencies 裡面加了B對應的Node
B對應的Node->nodeDependents 裡面加了C對於的Node
兩次循環歸總起來就是
A的Node:nodeDependencies 沒有數據,nodeDependents 裡面有B的Node
B的Node:nodeDependencies 裡面有A的Node,nodeDependents 裡面有C的Node
C的Node:nodeDependencies 裡面有B的Node,nodeDependents 沒有數據。
A不依賴那個動畫,B依賴A,C依賴B。同時這裡的依賴關系是Dependency.AFTER, AnimatorSet.playSequentially()的分析就到此結束了。

二,AnimatorSet.playTogether() 函數裡面干了些啥

playTogether函數源代碼

    public void playTogether(Animator... items) {
        if (items != null) {
            mNeedsSort = true;
            Builder builder = play(items[0]);
            for (int i = 1; i < items.length; ++i) {
                builder.with(items[i]);
            }
        }
    }

play函數看過了,直接看builder.with函數,這裡的builder每次都是同一個對象,Builder裡面的mCurrentNode一直是同一個是參數第一個動畫對應的Node哦。看下with函數裡面都干了啥。

        public Builder with(Animator anim) {
            Node node = mNodeMap.get(anim);
            if (node == null) {
                node = new Node(anim);
                mNodeMap.put(anim, node);
                mNodes.add(node);
            }
            Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
            node.addDependency(dependency);
            return this;
        }

和前面的before函數是一樣的就是換了依賴關系,換成了Dependency.WITH。
同樣的為AnimatorSet.playTogether()舉個例子。同意參數是三個動畫A,B,C, AnimatorSet.playTogether(A,B,C)。最後的結果是怎樣的呢。playTogether裡先產生了A對應的Node,然後做兩次循環。
第一次循環
B的Node->nodeDependencies 裡面加了A對應的Node
A對應的Node ->nodeDependents 裡面加了B對於的Node
第二次循環(Builder沒變,mCurrentNode也沒變)
C的Node->nodeDependencies 裡面加了A對應的Node
A對應的Node ->nodeDependents 裡面加了C對於的Node
歸總起來就是
A的Node: nodeDependencies 沒有數據,nodeDependents 裡面有B的Node,C的Node
B的Node: nodeDependencies 裡面有A的Node, nodeDependents 沒有數據
C的Node: nodeDependencies 裡面有A的Node, nodeDependents 沒有數據
B,C都依賴A。

三,AnimatorSet.start() 函數裡面干了些啥

老規矩進入start源碼。

    @Override
    public void start() {
        mTerminated = false;
        mStarted = true;
        mPaused = false;

        for (Node node : mNodes) {
            node.animation.setAllowRunningAsynchronously(false);
        }

        if (mDuration >= 0) {
            // If the duration was set on this AnimatorSet, pass it along to all child animations
            for (Node node : mNodes) {
                // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
                // insert "play-after" delays
                node.animation.setDuration(mDuration);
            }
        }
        if (mInterpolator != null) {
            for (Node node : mNodes) {
                node.animation.setInterpolator(mInterpolator);
            }
        }
        // First, sort the nodes (if necessary). This will ensure that sortedNodes
        // contains the animation nodes in the correct order.
        sortNodes();

        int numSortedNodes = mSortedNodes.size();
        for (int i = 0; i < numSortedNodes; ++i) {
            Node node = mSortedNodes.get(i);
            // First, clear out the old listeners
            ArrayList oldListeners = node.animation.getListeners();
            if (oldListeners != null && oldListeners.size() > 0) {
                final ArrayList clonedListeners = new
                        ArrayList(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 nodesToStart = new ArrayList();
        for (int i = 0; i < numSortedNodes; ++i) {
            Node node = mSortedNodes.get(i);
            if (mSetListener == null) {
                mSetListener = 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(this, node, dependency.rule));
                }
                node.tmpDependencies = (ArrayList) node.dependencies.clone();
            }
            node.animation.addListener(mSetListener);
        }
        // Now that all dependencies are set up, start the animations that should be started.
        if (mStartDelay <= 0) {
            for (Node node : nodesToStart) {
                node.animation.start();
                mPlayingSet.add(node.animation);
            }
        } else {
            mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
            mDelayAnim.setDuration(mStartDelay);
            mDelayAnim.addListener(new AnimatorListenerAdapter() {
                boolean canceled = false;
                public void onAnimationCancel(Animator anim) {
                    canceled = true;
                }
                public void onAnimationEnd(Animator anim) {
                    if (!canceled) {
                        int numNodes = nodesToStart.size();
                        for (int i = 0; i < numNodes; ++i) {
                            Node node = nodesToStart.get(i);
                            node.animation.start();
                            mPlayingSet.add(node.animation);
                        }
                    }
                    mDelayAnim = null;
                }
            });
            mDelayAnim.start();
        }
        if (mListeners != null) {
            ArrayList tmpListeners =
                    (ArrayList) mListeners.clone();
            int numListeners = tmpListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationStart(this);
            }
        }
        if (mNodes.size() == 0 && mStartDelay == 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
            mStarted = false;
            if (mListeners != null) {
                ArrayList tmpListeners =
                        (ArrayList) mListeners.clone();
                int numListeners = tmpListeners.size();
                for (int i = 0; i < numListeners; ++i) {
                    tmpListeners.get(i).onAnimationEnd(this);
                }
            }
        }
    }

函數還蠻長的,
11-18行,如果AnimatorSet設置了duration,那麼就統一設置所有動畫的duration,單個設置的就沒有效果了。
19-23行,如果AnimatorSet設置了插值器,那麼就統一設置所有動畫的插值器,單個的設置就沒有效果了。
26行,sortNodes(); 這個函數我們不看了,就是根據對應關系去拍下,最後排序的結果會放到mSortedNodes中去。
29-44行,如果有的動畫設置了DependencyListener或者AnimatorSetListener,把對應的監聽移除掉,因為這兩個對應的Listener在AnimatorSet裡面要用來決定動畫的播放順序的。不讓單個的動畫去設置。
55-68行,去遍歷整個的動畫列表,56-58行,如果動畫的Node沒有dependencies,那麼把這個動畫加入到nodesToStart的list裡面去。
58-66行,如果動畫有依賴的動畫,遍歷這個動畫依賴的動畫(dependencies)。給這些依賴的動畫添加DependencyListener的監聽。這個是順序或者同時播放的關鍵。順序播放靠監聽DependencyListener的onAnimationEnd啟動下一個動畫,同時播放則是靠監聽DependencyListener的onAnimationStart來啟動下一個動畫,這個好理解吧,同時播放啟動的時候同時起來,順序播放結束的時候啟動下一個。這個我們等下再分析。繼續往下看start的函數。
70-75行,AnimatorSet沒有設置延時 啟動nodesToStart裡面所有的動畫。
75-96行,AnimatorSet設置了延時 加入了一個臨時的動畫,duration就設置成了延時的時間,當這個動畫結束的時候啟動nodesToStart裡面所有的動畫。
剩下的代碼應該也好理解,對AnimatorSet的Listener的處理(主要哦是這裡說的是設置給AnimatorSet的監聽哦)。

接下裡繼續上面沒分析的一個點DependencyListener,這個裡面才是動畫順序,同時播放的關鍵點。
看AnimatorSet裡面的內部類DependencyListener 這個類裡面的onAnimationEnd和onAnimationStart兩個函數。一個是管順序播放的一個是管同時播放的這個好理解吧。

        public void onAnimationEnd(Animator animation) {
            if (mRule == Dependency.AFTER) {
                startIfReady(animation);
            }
        }
        public void onAnimationStart(Animator animation) {
            if (mRule == Dependency.WITH) {
                startIfReady(animation);
            }
        }

都調用了startIfReady函數。

        private void startIfReady(Animator dependencyAnimation) {
            if (mAnimatorSet.mTerminated) {
                // if the parent AnimatorSet was canceled, then don't start any dependent anims
                return;
            }
            Dependency dependencyToRemove = null;
            int numDependencies = mNode.tmpDependencies.size();
            for (int i = 0; i < numDependencies; ++i) {
                Dependency dependency = mNode.tmpDependencies.get(i);
                if (dependency.rule == mRule &&
                        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;
                }
            }
            mNode.tmpDependencies.remove(dependencyToRemove);
            if (mNode.tmpDependencies.size() == 0) {
                // all dependencies satisfied: start the animation
                mNode.animation.start();
                mAnimatorSet.mPlayingSet.add(mNode.animation);
            }
        }

這裡估計看代碼的時候會比較亂的,我們就用例子來說明,還是舉剛才的AnimatorSet.playSequentially()傳入三個動畫A,B,C。依賴關系是
A的Node:nodeDependencies 沒有數據,nodeDependents 裡面有B的Node
B的Node:nodeDependencies 裡面有A的Node,nodeDependents 裡面有C的Node
C的Node:nodeDependencies 裡面有B的Node,nodeDependents 沒有數據。
在start函數裡面,0-0行,遍歷到A動畫的時候,因為A的Node沒有nodeDependencies,所以A被加入到了nodesToStart的list裡面去了,遍歷到B動畫的時候,B有nodeDependencies,然後給A動畫加了DependencyListener的Listener注意哦這個是給A動畫加了DependencyListener哦。在添加DependencyListener的時候把B的Node傳遞過去了。因為我們的規則是Dependency.AFTER,所以在A動畫結束的時候會到DependencyListener的onAnimationEnd函數裡面去,就走到了startIfReady函數了。這裡就要啟動B的動畫了,看看是怎麼啟動的。
8-18行,遍歷B的Node的nodeDependencies,找到A,把A的DependencyListener移除掉,A的DependencyListener沒有用了。
20-24行,啟動B的動畫,這樣在A動畫播放完之後B的動畫也啟動起來了。
整個就結束了。

總結來說就是確定動畫間的依賴關系,然後通過動畫的DependencyListener按一定的順序啟動動畫。

流水賬記完了,下一遍估計就是插值器 估值器 和一些例子。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved