上期文章鎮樓:
這可能是第二好的自定義 View 教程之繪製
凱哥的文章確實寫的細而好呀,這不,活生生把 面試系列 先放一放,繼續講解我們的動畫。
為啥是第二好?
一看就是沒看 前面的文章 的。這裡就不解釋啦。
不是講所有動畫
Android 裡面對動畫可以進行一些分類,主要分為兩類:
- Animation
- Transition
由於 「Transtion」 重點在於切換而不是動畫,所以我們今天直接忽略。廢話不用多說,那麼我們就直接講解屬性動畫「Property Animation」吧。
現在的專案中的動畫 99% 都是用的屬性動畫,所以我們不講 View Animation。
ViewPropertyAnimator
這一塊比較簡單,我們可以直接引用凱哥這裡的總結圖。(凱哥文章,業界良心,真的很贊。)
從圖中可以看到, View 的每個方法都對應了 ViewPropertyAnimator 的兩個方法,其中一個是帶有 -By 字尾的,例如,View.setTranslationX()
對應了 ViewPropertyAnimator.translationX()
和 ViewPropertyAnimator.translationXBy()
這兩個方法。其中帶有 -By()
字尾的是增量版本的方法,例如,translationX(100)
表示用動畫把 View 的 translationX
值漸變為 100,而 translationXBy(100)
則表示用動畫把 View 的 translationX 值 漸變地增加 100。
其中的 ViewPropertyAnimator
可以通過 View.animate()
得到。
/**
* 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;
}複製程式碼
ObjectAnimator
使用方式:
- 如果是自定義控制元件,需要新增
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 多個動畫配合執行
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,再降回了實際的值。這利用上面所提到的知識,好像根本沒法實現,這效果有意思,我們看看怎麼實現。
PropertyValuesHolders.ofKeyframe() 把同一個屬性拆分
除了合併多個屬性和調配多個動畫,你還可以在 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
實際上不太想說這個 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 ,儘自己所能為你提升。如果你喜歡,為我點贊分享吧~