最近年底了,打算把自己的Android知識都整理一下。
Android技能書系列:
Android基礎知識
Android技能樹 — Android儲存路徑及IO操作小結
資料結構基礎知識
演算法基礎知識
最近整理了下自己學過的動畫方面的知識。用百度腦圖做了動畫知識的思維腦圖,哪裡如果覺得不對,大家可以留言提出哦。
你沒看錯,掘金的文章的圖片,電腦上看這種思維腦圖根本就看不清楚,所以我準備一塊塊來講。 (掘金手機版APP倒是可以放大,看的挺清晰的。)
總結的圖已經傳到了Github上面,可以下載:
動畫可以分為兩類:Animation 和 Transition二類。
Animation
因為一般來說第二塊Animation用的比較多,所以我們先來看Animation這塊:
好的,我們可以看到我們的Animation可以分成:
- 幀動畫
- View動畫
- Property動畫(屬性動畫)
我們可以按順序一個個來看:
幀動畫:
幀動畫我就不多說了,就是提前準備好一個連續的圖片,然後一張張切換,就類似gif圖播放一樣。注意點就是圖片數量過多並且圖片較大,容易出現OOM。View動畫:
1. 四種基本動畫:
我們可以看到,其實View動畫很簡單,基本使用的是“平移”,“縮放”,“旋轉”,“透明度”四種基本動畫。
2. LayoutAnimation 及 介面切換動畫:
然後我們看特殊場景下的View動畫:
LayoutAnimaion : 在ViewGroup中,View動畫可以用來控制子元素的出場效果,比如我們的應用中的列表,我們在載入列表中的子項的時候,可以讓item載入的時候不是突然出現,可以伴隨各種動畫。比如:
這裡的介面切換動畫,與最剛開始的大分類的Transition不同,這裡的過渡的動畫用的是View動畫,比如Activity的切換效果:
//當啟動一個Activity時
Intent intent = new Intent(this,XXXXX.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
//當Activity退出時
@Override
public void finish(){
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}
複製程式碼
其中我們可以看到主要是用到overridePendingTransition
方法,這個方法必須放在startActivity()
和finish()
後面。
3. View動畫注意事項:
這裡我們可以看到,View動畫其實並不是真得改變了View的狀態,比如說我們寫了一個按鈕,點選按鈕可以Toast一段內容,通過Translate動畫從左邊平移到了右邊,這時候雖然按鈕看上去在右邊了,但是這時候你點選按鈕,並不會出現Toast的內容,但是你點選左邊的按鈕初始位置,卻有Toast內容。因為其實按鈕只是影像移動過去而已。真正的按鈕還是在原始位置。也許有人會問,那如果我就是希望按鈕移動到右邊後,點選右邊的按鈕可以有點選事件,你可以選擇後面提到的屬性動畫,或者如果你一定要用View動畫,那你可以在右邊目標位置,提前準備一個一模一樣的並且隱藏的按鈕,然後當左邊的按鈕移動到右邊後,我們可以設定右邊的隱藏的按鈕出現,然後把左邊的最初的按鈕進行隱藏即可。
屬性動畫:
首先大家可以看下扔物線大佬的相關這個知識點的文章:
HenCoder Android 自定義 View 1-6: 屬性動畫(上手篇)
【HenCoder Android 開發進階】自定義 View 1-7:屬性動畫(進階篇)
1. ViewPropertyAnimator:
我直接引用了扔物線大佬文章裡面的相關動畫操作的圖片:
用ViewPropertyAnimator 來做屬性動畫是最簡單的。特別方便。
ViewPropertyAnimator多個動畫進行:
如果想多個動畫同時進行,只需要簡單的:
view.animate()
.scaleX(1)
.scaleY(1)
.alpha(1);
複製程式碼
2. ObjectAnimator:
引用扔物線大佬裡面的內容:
動畫操作使用方式:
- 如果是自定義控制元件,需要新增 setter / getter 方法;
- 用 ObjectAnimator.ofXXX() 建立 ObjectAnimator 物件;
- 用 start() 方法執行動畫。
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();
複製程式碼
ObjectAnimation多個動畫同時進行 - PropertyValuesHolder:
ObjectAnimation在多個動畫一起進行的時候不能像ViewPropertyAnimation那樣方便,不過你可以使用 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();
複製程式碼
PropertyValuesHolder
的意思從名字可以看出來,它是一個屬性值的批量存放地。所以你如果有多個屬性需要修改,可以把它們放在不同的 PropertyValuesHolder
中,然後使用 ofPropertyValuesHolder()
統一放進 Animator
。這樣你就不用為每個屬性單獨建立一個Animator
分別執行了。
PropertyValuesHolder - Keyframe:
PropertyValuesHolders.ofKeyframe()
把同一個屬性拆分
除了合併多個屬性和調配多個動畫,你還可以在 PropertyValuesHolder
的基礎上更進一步,通過設定 Keyframe
(關鍵幀),把同一個動畫屬性拆分成多個階段。
例如,你可以讓一個進度增加到 100% 後再「反彈」回來。
// 在 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();
複製程式碼
3. ValueAnimator:
ViewPropertyAnimator 和 ObjectAnimator的底部都是用ValueAnimator實現的,從字面意思就可以看出是數值的動畫,也就是數值的變化。比如:
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,5);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();//當前的值
}
});
複製程式碼
我們可以看到ValueAnimator是監聽到當前變化到哪個值了。然後你拿著這個值想怎麼處理,就是你的事了。所以ValueAnimator就更基礎。
所以你看,ViewPropertyAnimator、ObjectAnimator、ValueAnimator 這三種 Animator,它們其實是一種遞進的關係:從左到右依次變得更加難用,也更加靈活。但我要說明一下,它們的效能是一樣的,因為 ViewPropertyAnimator 和 ObjectAnimator 的內部實現其實都是 ValueAnimator,ObjectAnimator 更是本來就是 ValueAnimator 的子類,它們三個的效能並沒有差別。它們的差別只是使用的便捷性以及功能的靈活性。所以在實際使用時候的選擇,只要遵循一個原則就行:儘量用簡單的。能用 View.animator() 實現就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。
4. AnimationSet:
AnimationSet為什麼要把這個單獨拎出來呢。AnimationSet可以用在多個動畫播放,很多人就說了,上面我們在ViewPropertyAnimator 及ObjectAnimation中的PropertyValuesHolder已經可以用在多個動畫一起播放了嗎?沒錯,問題就出在這個<一起>這二個字上面,因為上面的二個都是隻能N個動畫同時播放,比如我現在的需求是先平移,然後平移結束後再放大和改變透明度。而AnimationSet及可以一起播放,又可以控制動畫的先後順序來。
ObjectAnimator animator1 = ObjectAnimator.ofFloat(...);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(...);
animator2.setInterpolator(new DecelerateInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
// 兩個動畫依次執行
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();
複製程式碼
使用 playSequentially(),就可以讓兩個動畫依次播放,而不用為它們設定監聽器來手動為他們監管協作。 AnimatorSet 還可以這麼用:
// 兩個動畫同時執行
animatorSet.playTogether(animator1, animator2);
animatorSet.start();以及這麼用:
// 使用 AnimatorSet.play(animatorA).with/before/after(animatorB)
// 的方式來精確配置各個 Animator 之間的關係
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
複製程式碼
有了 AnimatorSet ,你就可以對多個 Animator 進行統一規劃和管理,讓它們按照要求的順序來工作。
5. 插值器 與 估值器
插值器(Interpolator)和 估值器(Evaluator)大家肯定也見過很多次,那這二個到底是用來幹嘛的呢?
我們從頭來分析一個平移的動畫:
- 我們告訴一個View,5秒內,從左到右移動500px的距離。
- 時間經歷到了 N 秒的時候,我們要知道整個動畫到了哪個程度,比如動畫執行了50% 了。
- 當動畫執行到某個程式的時候(比如執行了50%),這時候我們的X軸的移動距離的值具體是多少px。
- 然後View通過獲取到的具體的移動距離的px值,去設定View的
translationX
的屬性,讓View去移動。
那我們的插值器和估值器是用在哪裡呢:
插值器是用在第二步裡面,時間經歷了N秒,我們返回一個值,這個值是說明當前動畫進行到哪個程度了。
估值器是用在第三步,我們已經知道了動畫執行到了哪個程式,然後我們返回具體的當前變化的數值。
比如我們來看LinearInterpolator.java
和IntEvaluator.java
的原來再理解下:
public class LinearInterpolator implements Interpolator{
public LinearInterpolator(){
}
public LinearInterpolator(Context context,AttributeSet attrs){
}
public float getInterpolation(float input){
//比如時間是5秒,這時候過了2.5秒,這時候時間流逝了0.5,所以input是0.5
//因為我們這個LiearInterpolator是線性插值器,
//所以時間流失了0.5,我們的動畫的動畫也執行了0.5(也就是完成了50%的程度)
//所以這裡直接返回input即可。
return input;
}
}
複製程式碼
public class IntEvaluator implements TypeEvaluator<Integer>{
public Integer evaluate(float fraction,Integer startValue,Integer endValue){
//fraction就是我們上面插值器返回的,告訴我們動畫執行到什麼程度了。
//startValue是我們起始值,endValue是我們最後的目標值。
//比如我們是ofInt(0,500);這時候我們return的值就是(0+0.5 * (500-0))= 250
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startValue));
}
}
複製程式碼
Transition
過於過渡,我也不費心思全部很詳細的寫出來,網上的基本介紹及使用有很多。
基本知識:
Android 過渡(Transition)動畫解析之基礎篇
稍微深度過渡動畫基本原理:
Activity和Fragment Transition介紹
深入理解共享元素變換(Shared Element Transition)-上