上期文章鎮樓:
這多是第二好的自定義 View 教程之繪製java
凱哥的文章確實寫的細而好呀,這不,活生生把 面試系列 先放一放,繼續講解咱們的動畫。面試
Android 裏面對動畫能夠進行一些分類,主要分爲兩類:canvas
Animation微信
Transitionide
因爲 「Transtion」 重點在於切換而不是動畫,因此咱們今天直接忽略。廢話不用多說,那麼咱們就直接講解屬性動畫「Property Animation」吧。性能
如今的項目中的動畫 99% 都是用的屬性動畫,因此咱們不講 View Animation。動畫
這一塊比較簡單,咱們能夠直接引用凱哥這裏的總結圖。(凱哥文章,業界良心,真的很贊。)ui
從圖中能夠看到, View 的每一個方法都對應了 ViewPropertyAnimator 的兩個方法,其中一個是帶有 -By 後綴的,例如,View.setTranslationX()
對應了 ViewPropertyAnimator.translationX()
和 ViewPropertyAnimator.translationXBy()
這兩個方法。其中帶有 -By()
後綴的是增量版本的方法,例如,translationX(100)
表示用動畫把 View 的 translationX
值漸變爲 100,而 translationXBy(100)
則表示用動畫把 View 的 translationX 值 漸變地增長 100。this
其中的 ViewPropertyAnimator
能夠經過 View.animate()
獲得。spa
/** * This method returns a ViewPropertyAnimator object, which can be used to animate * specific properties on this View. * * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View. */ public ViewPropertyAnimator animate() { if (mAnimator == null) { mAnimator = new ViewPropertyAnimator(this); } return mAnimator; }
使用方式:
若是是自定義控件,須要添加 setter
/ getter
方法;
用 ObjectAnimator.ofXXX()
建立 ObjectAnimator
對象;
用 start()
方法執行動畫。
其中特別須要注意的是:
setter()
方法須要調用 invalidate()
對 View 進行重繪。
**獲取 ObjectAnimator
採用的是 ObjectAnimator.ofXXX()
方法。
至因而 「ofFloat」仍是「ofInt」仍是別的,這個徹底視你的 View 參數而定,而且第二個參數用 setXXX
的「XXX」字符串。**
上面 ViewPropertyAnimator
方法基本都是通用的。
固然,當你看到 ObjectAnimator.ofObject()
方法的時候,你會心生疑惑,這其實就是爲了對不限定類型的屬性作動畫。
例子也懶得寫了,直接用凱哥的。
public class SportsView extends View { float progress = 0; ...... // 建立 getter 方法 public float getProgress() { return progress; } // 建立 setter 方法 public void setProgress(float progress) { this.progress = progress; invalidate(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); ...... canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint); ...... } } ...... // 建立 ObjectAnimator 對象 ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65); // 執行動畫 animator.start();
固然,這些動畫都是能夠自由組合的,支持「鏈式調用」,由於它們返回的都是 ViewPropertyAnimator
。
好比這樣。
view.animate() .scaleX(1) .scaleY(1) .alpha(1);
而對於 ObjectAnimator
,是不能這麼用的。不過你可使用 PropertyValuesHolder
來同時在一個動畫中改變多個屬性。
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1); PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1); PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3) animator.start();
從上面的 gif 圖能夠發現,動畫是同步進行的,那要是咱們但願依次執行怎麼辦?好比這樣,先放大再平移。
萬能的 Android 天然難不倒咱們,這樣就有了 AnimatorSet
。
AnimatorSet.playSequentially(Animator... items)
完美地解決了咱們上面的疑惑。
AnimatorSet animatorSet = new AnimatorSet(); // 兩個動畫依次執行 animatorSet.playSequentially(animator1, animator2); animatorSet.start();
其中 「animator1」和「animator2」分別是放大和平移的動畫。
翻閱官方源碼一看,其實不止 playSequentially()
一個方法,除了順序執行,固然有其餘方法,好比:
AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(animator1).before(animator2); // 先執行 1 再執行 2 animatorSet.playTogether(animator2, animator3); // 2 和 3 同時開始 animatorSet.start();
相似的處理方案,還不少不少方式,具體你能夠查看官方源碼。
僅靠這些方法作出來的動畫效果說實話已經很炫了,不過咱們老是不知足,好比咱們設計師想這樣怎麼辦?
圖片中先是將進度填到了 100,再降回了實際的值。這利用上面所提到的知識,好像根本無法實現,這效果有意思,咱們看看怎麼實現。
除了合併多個屬性和調配多個動畫,你還能夠在 PropertyValuesHolder
的基礎上更進一步,經過設置 Keyframe
(關鍵幀),把同一個動畫屬性拆分紅多個階段。好比,要實現上面的效果,你只需:
// 在 0% 處開始 Keyframe keyframe1 = Keyframe.ofFloat(0, 0); // 時間通過 50% 的時候,動畫完成度 100% Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100); // 時間見過 100% 的時候,動畫完成度倒退到 80%,即反彈 20% Keyframe keyframe3 = Keyframe.ofFloat(1, 80); PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder); animator.start();
先小結一下,「關於複雜的屬性關係來作動畫」,就這麼三種:
使用 PropertyValuesHolder
來對多個屬性同時作動畫;
使用 AnimatorSet
來同時管理調配多個動畫;
PropertyValuesHolder
的進階使用:使用 PropertyValuesHolder.ofKeyframe()
來把一個屬性拆分紅多段,執行更加精細的屬性動畫。
實際上不太想說這個 ValueAnimator
,由於它的使用場景確實很少。這裏也不精挑細琢了,大概須要記得:
ViewPropertyAnimator
和ObjectAnimator
的內部實現其實都是ValueAnimator
,ObjectAnimator
更是原本就是ValueAnimator
的子類,它們三個的性能並無差異。它們的差異只是使用的便捷性以及功能的靈活性。因此在實際使用時候的選擇,只要遵循一個原則就行:儘可能用簡單的。能用 View.animate() 實現就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。
另外,它們還支持 setDuration(long duration)
設置動畫持續時長以及 setInterpolator()
設置各類「速度設置器」。
「速度設置器」,簡而言之就是動畫的「速度模型」,這個方法可讓動畫按照你想要的方式進行。通常狀況咱們都直接不設置個,因此下面的瞭解一下就好,甚至跳過也沒事。
簡單科普一下提供的「速度模型」,須要看效果的 點擊這裏。
AccelerateDecelerateInterpolator
先加速再減速。這是默認的「速度模型」,實際上就像開車同樣,先加速啓動,開一會再減速剎車停下。
LinearInterpolator
勻速模型,即動畫是迅速進行的。
AnticipateOvershootInterpolator
帶施法前搖和回彈的「速度模型」。
DecelerateInterpolator
持續減速到 0,動畫開始的時候是最高速度,而後在動畫過程當中逐漸減速,直到動畫結束的時候剛好減速到 0。
AccelerateInterpolator
持續加速。和上面那個剛剛相反。
AnticipateInterpolator
先回拉一下再進行正常動畫軌跡。效果看起來有點像投擲物體或跳躍等動做前的蓄力。
OvershootInterpolator
動畫會超過目標值一些,而後再彈回來。效果看起來有點像你一屁股坐在沙發上後又被彈起來一點的感受。
AnticipateOvershootInterpolator
上面這兩個的結合版:開始前回拉,最後超過一些而後回彈。
他孃的,太多啦,就不一一寫完了,真沒啥用處,用到再說嘛。
能夠給動畫設置監聽器,分別有兩種設置方式。
ViewPropertyAnimator.setListener(AnimatorListener listener)
ObjectAnimator.addListener(AnimatorListener listener)
能夠看到,兩種動畫方式設置監聽器有一點不一樣的是一個是 「set」,一個是「add」,但它們的參數都是一致的「AnimatorListener」。
AnimatorListener
有四個回調方法:
/** * <p>Notifies the start of the animation.</p> * * @param animation The started animation. */ void onAnimationStart(Animator animation); /** * <p>Notifies the end of the animation. This callback is not invoked * for animations with repeat count set to INFINITE.</p> * * @param animation The animation which reached its end. */ void onAnimationEnd(Animator animation); /** * <p>Notifies the cancellation of the animation. This callback is not invoked * for animations with repeat count set to INFINITE.</p> * * @param animation The animation which was canceled. */ void onAnimationCancel(Animator animation); /** * <p>Notifies the repetition of the animation.</p> * * @param animation The animation which was repeated. */ void onAnimationRepeat(Animator animation);
從代碼中能夠很清晰的看出:
在動畫開始執行的時候,調用 onAnimationStart()
在動畫結束後,將調用 onAnimationEnd()
在動畫取消後,將調用 onAnimationCancel()
在動畫重複執行的時候,將調用 onAnimationRepeat()
其中只須要注意兩點:
動畫被 cancel()
取消的時候,依然會調用 onAnimationEnd()
,不過是在 onAnimationCancel()
以後。
**重複執行動畫,經過 setRepeatMode()
/ setRepeatCount()
或者 repeat()
方法執行。
但可是!!!ViewProperAnimator
不支持重複。**
除了上面設置的動畫生命週期監聽器,咱們還有其餘的方法,好比 ViewPropertyAnimator.setUpdateListener()
/ ObjectAnimator.addUpdateListener()
。
這兩個方法雖然名稱和可設置的監聽器數量不同,但本質其實都同樣的,它們的參數都是 AnimatorUpdateListener
。它只有一個回調方法:onAnimationUpdate(ValueAnimator animation)
。
/** * Implementors of this interface can add themselves as update listeners * to an <code>ValueAnimator</code> instance to receive callbacks on every animation * frame, after the current frame's values have been calculated for that * <code>ValueAnimator</code>. */ public static interface AnimatorUpdateListener { /** * <p>Notifies the occurrence of another frame of the animation.</p> * * @param animation The animation which was repeated. */ void onAnimationUpdate(ValueAnimator animation); }
當動畫的屬性更新時(不嚴謹的說,即每過 10 毫秒,動畫的完成度更新時),這個方法被調用。
方法的參數是一個 ValueAnimator
,ValueAnimator
是 ObjectAnimator
的父類,也是 ViewPropertyAnimator
的內部實現,因此這個參數其實就是 ViewPropertyAnimator
內部的那個 ValueAnimator
,或者對於 ObjectAnimator
來講就是它本身自己。
仍是作個小結,自定義 View 中咱們使用屬性動畫主要分爲三種方式:
ViewPropertyAnimator
ObjectAnimator
ValueAnimator
使用它們的任一個都不會有性能差別,只需記住一個原則,依次愈來愈難,能用簡單的就用簡單的。
哦,還有一個最重要的原則就是,盯緊「扔物線凱哥」,加入咱們的「HenCoder」大軍吧。
作不完的開源,寫不完的矯情。歡迎掃描下方二維碼或者公衆號搜索「nanchen」關注個人微信公衆號,目前多運營 Android ,盡本身所能爲你提高。若是你喜歡,爲我點贊分享吧~