屬性動畫
屬性動畫(Property Animation) 是 API Level 11
時被引入的,Android 3.0
纔開始有 屬性動畫相關的 API.
先來看看網上的一個開源控件,動畫還是很酷炫,基本包含了屬性動畫該有的大部分知識內容.
1. ValueAnimator
ValueAnimator是屬性動畫的核心類,下面會講到 TimeAnimator
, ObjectAnimator
就是它的子類。
調用的幾個方式:
ofObject是直接把一個對象過渡到另外一個對象,其它的 ofArgb/ofFloat/ofInt 是將值過渡到另一個值
ValueAnimator.ofObject(TypeEvaluator evaluator/*估值器,下個章節有介紹*/, Object... values)
ValueAnimator.ofArgb(int... values)
ValueAnimator.ofFloat(float.. values)
ValueAnimator.ofInt(int... values)
ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder... values)
舉個栗子:
// ValueAnimator.ofObject demo
ValueAnimator objectAnimator = ValueAnimator.ofObject(
new PointEvaluator(), new Point(10, 10), new Point(100, 100));
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
objectAnim.setDuration(2000);
objectAnim.setRepeatCount(ValueAnimator.INFINITE);
objectAnim.setRepeatMode(ValueAnimator.RESTART);
objectAnim.start(); // 啓動動畫
# 刷新UI繪製界面
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(mPoint.x, mPoint.y, 50, mPaint);
}
涉及的知識點:
函數名 | 說明 |
---|---|
Animator.start() |
啓動動畫 |
Animator.end() |
結束動畫 |
Animator.cancel() |
取消動畫 |
Animator.pause() |
暫停動畫 API 19新增的方法 |
Animator.resume() |
重新啓動 API 19新增的方法 |
Animator.setDuration(int) |
設置動畫時間 |
setRepeatCount(int) |
動畫重複次數(大於等於 0),值爲 小於0 或 ValueAnimator.INFINITE 時,無限循環 |
setRepeatMode |
設置循環模式,ValueAnimator.RESTART: 重新從頭開始執行 ;ValueAnimator.REVERSE:反方向執行 |
addUpdateListener |
監聽數值變化, ValueAnimator.AnimatorUpdateListener -> onAnimationUpdate 通過數值返回進行一序列相關操作 |
動畫監聽器 addListener |
Animator.AnimatorListener 相關函數說明: onAnimationStart() - 在動畫開始播放時調用;onAnimationEnd() - 在動畫結束播放時調用/取消的動畫也會調用; onAnimationRepeat() - 在動畫重複播放時調用;onAnimationCancel() - 在動畫取消播放時調用; |
像幾年前在公司寫的輸入法的移動邊框的動畫,就是用的 ValueAnimator + onDraw繪製(onAnimationUpdate更新相關值) 出來的動畫效果
其實有一些酷炫的動畫(當然這裏你需要了解android繪圖的相關知識(比如繪圖,混合模式,Canvas,Paint等等),也是用這種方式弄出來的.
還有一個很酷炫的爆炸粒子效果
相關資料學習地址
1.1 ObjectAnimator
ValueAnimator 有一個缺點,如果對某個控件執行動畫,就需要監聽 ValueAnimator 的動畫過程,相比補間動畫(View動畫)要繁瑣的多。
爲了調用簡單方便的去 動畫對應的控件,所以就產生了 ObjectAnimator(繼承自ValueAnimator,所以ValueAnimator的函數ObjectAnimator都可以調用
) ;
ObjectAnimator 重寫了幾個函數,ofInt()
, ofFloat(object target, String propertyName, float... values)
,ofArgb()
等等;來看看函數的使用方式的小栗子;
實現了 放大 X 的動畫
// 第一個參數傳入需要做動畫的對象(target) 第二個參數 是屬性,第三個參數是 可變參數,是值.
// 現在就是 放大 X,從 1.0f ~ 2.4f
// 之所以可以動畫,是因爲 view 裏面有 setScaleX(float scaleX) 這個函數,ObjectAnimator對這個 view 的 scaleX 自動賦值.
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.SCALE_X/*scaleX*/, 1.0f, 2.4f);
animator.setDuration(300);
animator.start();
ObjectAnimator動畫流程瞭解
ofFloat(view, “scaleX”, 1.0f, 2.4f) -> 插值器 -> 估值器 -> 調用set函數(主要是反射+拼裝)
可以改變的控件屬性列表:scaleX
,scaleY
,translationX
,translationY
,alpha
,rotation
等等。(主要是類裏面可以設置的屬性值,都可以改變,需要有 setXXX
纔行)
旋轉
我們還可以自定義ObjectAnimator
- 爲對象設置需要操作屬性的
set()
與get()
方法 - 通過實現
TypeEvaluator(估值器)
類從而定義屬性變化的邏輯(下下個章節有介紹)
來個小栗子(我們修改剛纔的ValueAnimatror小球的移動的代碼)
// 1. 給小球的View的類 添加了一個帶 SetXXX 方式的函數
public void setPointX(int x) {
mPoint.x = x;
invalidate();
}
// 2. 因爲是 int 類型,所以使用 ofInt
ObjectAnimator testAnim = ObjectAnimator.ofInt(view, "PointX", 100, 500);
testAnim.setDuration(888);
testAnim.start();
注意
如果想要改變控件的寬(Width),高(Height),是不行,因爲setWidth不是改變的控件的寬度,所以需要這樣改.
private static class ViewWrapper {
private View mTarget;
// 構造方法:傳入需要包裝的對象
public ViewWrapper(View target) {
mTarget = target;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
... ..
}
public void setHeight(int height) ...
... ...
}
// 調用方式
ObjectAnimator widthAnim = ObjectAnimator.ofInt(new ViewWrapper(view), "width", 100, 500);
1.2 TimeAnimator 與 其它
TimeAnimator(繼承自 ValueAnimator
) 不常用,此處不做篇幅介紹,感興趣的小夥伴,請自行查閱官方文檔或者搜索相關資料.
PropertyValuesHolder
官方文檔資料
指定關鍵幀 Keyframe.ofFloat(float fraction, float value)
,(類似動畫的關鍵幀,包含兩個元素,時間點和位置)
// 小DEMO
// 生成了三個KeyFrame對象
// fraction:表示當前的顯示進度,即從加速器中getInterpolation()函數的返回值;
// value:表示當前應該在的位置
KeyFrame frame0 = Keyframe.ofFloat(0f, 10f);
KeyFrame frame1 = Keyframe.ofFloat(0.1f, 120f);
KeyFrame frame3 = Keyframe.ofFloat(1.0f, 0f);
//
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", frame0, frame1, frame2);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, rotationHolder);
animator.setDuration(3000);
animator.start();
Android 3.1 補充了 ViewPropertyAnimator 官方文檔資料
// 谷歌官方爲了便捷人性化的使用動畫,Android 3.1 支持ViewPropertyAnimator 的方式.
view.animate().scaleX(1.2f).scaleY(1.2f).setDuration(300).start();
// 還包含(alhpa,translationX/Y,rotationX/Y....),具體參考文檔,這裏不進行詳細的概述~!!
AnimatorSet
前面講解的 ValueAnimator 與 ObjectAnimator,TimeAnimator 只能單獨實現一個動畫,如果我們想弄出複雜的動畫效果,就需要用到 AnimatorSet
將他們串聯起來。
如何去串聯,這裏涉及到兩個重要的函數:
playSequentially(Animator... items | List<Animator> items):
順序執行動畫效果,這種情況類似,一分鐘內 事情只能一個個來做playTogether(Animator... items | Collection<Animator> items):
同時並行執行動畫效果,這種情況類似,在1分鐘內 同時可以做幾個事情
其它相關函數了解
類名 | 說明 |
---|---|
setDuration(long duration) | 設置動畫時長 |
setInterpolator | 設置插值器 |
setTarget(Object target) | 設置目標對象,需要做動畫的對象 |
舉個栗子(模仿開始的酷炫菜單 搞一個小DEMO(菜單展開).
講解下這個小demo的思路,
- 每個childView 添加 翻轉 + 透明 到 集合Set.(注意,這裏是同時並行執行動畫效果,使用的playTogether)
- 將所有 childView 的 集合set,添加到 總的集合allSet(這裏是順序執行動畫效果,使用的 playSequentially).
- 最終的效果就是,childView 一個個順序 的 同時在 翻轉+透明
// 初始化位置
for (int i = 1; i < mRootLayout.getChildCount(); i++) {
View view = mRootLayout.getChildAt(i);
view.setPivotY(0);
view.setRotationX(-90);
}
// 動畫集合
AnimatorSet allSet = new AnimatorSet();
List<Animator> animList = new ArrayList<>();
for (int i = 1; i < mRootLayout.getChildCount(); i++) {
View childView = mRootLayout.getChildAt(i);
AnimatorSet set = new AnimatorSet();
// 同時並行執行 翻轉 + 透明 的動畫效果,加入集合 set
set.playTogether(rotationOpenVertical(childView), rotationOpenAlpha(childView));
animList.add(set);
}
// 順序執行 childView 的動畫效果,加入集合 allSet
allSet.setInterpolator(new AccelerateInterpolator());
allSet.playSequentially(animList);
allSet.setDuration(388);
allSet.start();
// 旋轉
public static ObjectAnimator rotationOpenVertical(View v) {
return ObjectAnimator.ofFloat(v, "rotationX", -90, 0);
}
// 透明
public static ObjectAnimator rotationOpenAlpha(View v) {
return ObjectAnimator.ofFloat(v, View.ALPHA, 0, 1);
}
插值器(Interpolator)
設置插值器 - setInterpolator
插值器(加速器)說白了,就是 控制變化速率(指定動畫如何變化變量) 的,比如 1秒(1000ms) 內 的 0 ~ 400 的距離,我們可以讓這 0 ~ 400 區間 不是 勻速的,有快有慢,這就是插值器的作用;
除了下列的官方自帶的插值器(參考鏈接1,參考鏈接2),插值器也可以進行自定義;
類名 | 說明 |
---|---|
AccelerateDecelerateInterpolator | 加速減速插值器,該插值器的變化速率在 開始和結束時緩慢, 中間加速。 |
AccelerateInterpolator | 加速插值器,動畫開始的地方速率改變比較慢,然後開始加速 |
DecelerateInterpolator | 減速插值器,動畫開始的一瞬間加速到最大值,然後逐漸變慢 |
LinearInterpolator | 線性插值器,勻速加速器 |
BounceInterpolator | 彈跳插值器,模擬自由落地後回彈的效果 |
AnticipateInterpolator | 初始偏移插值器,開始時 反方向移動一段距離 ,然後繼續動畫 |
OvershootInterpolator | 結束偏移插值器,結束時,超出結束位置,然後再回到結束位置 |
CycleInterpolator | 循環插值器,該插值器的動畫會在指定數量的週期內重複。 |
AnticipateOvershootInterpolator | AnticipateInterpolator + OvershootInterpolator 的組合; |
TimeInterpolator | 該接口自定義自己的插值器。 |
舉個栗子
不設置插值器,默認是 LinearInterpolator(勻速),看下效果
以控件順序設置,進行插值器的設置,同樣的時間3秒,同樣的移動距離(translationX) 0~500
new AccelerateDecelerateInterpolator(); //(紅色) 加速減速插值器,該插值器的變化速率在 開始和結束時緩慢, 中間加速
new DecelerateInterpolator(); //(黃色) 減速插值器,動畫開始的一瞬間加速到最大值,然後逐漸變慢
new AccelerateInterpolator(); //(綠色) 加速插值器,動畫開始的地方速率改變比較慢,然後開始加速
new LinearInterpolator(); //(藍色) 線性插值器,勻速加速器
new AnticipateInterpolator(); //(x色) 初始偏移插值器,開始時 反方向移動一段距離 ,然後繼續動畫
從例子可以看出,插值器,主要是給動畫增加了變化的速率
,讓動畫更有節奏,更好看一些,就像我們跑步一樣,有時快,有時慢;
AnticipateInterpolator 的解析
// 原來是套入了一個數學公式 input * input * ((2.0f + 1) * input - 2.0f) 做了變化的速率
// 這樣的話,如果需要弄一些其它的效果,就可以進行自定義插值器
public float getInterpolation(float input) {
float tension = 2.0f;
float value = input * input * ((tension + 1) * input - tension);
return value;
}
自定義插值器小栗子
// 繼承 TimeInterpolator,重寫 getInterpolation 函數,input 取值(0.0~1.0)
public class TestInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
return (float) (Math.log10(1 + 9 * input));
}
}
估值器(Evaluator)
插值器我們已經瞭解,是設置 變化速率的,那麼估值器(Evaluator)是幹什麼的呢?看個正常效果的移動效果!!
再看看 增加了貝塞爾曲線公式的TypeEvaluator,看下效果,有拋物線的效果
從效果圖,我們可以看出,估值器是改變了 具體數值;
用於控制動畫如何從開始過渡到結束的,如:A(0,0) B(0,5)過兩點之間的線可以是一條直線運動,也能是一條曲線,這個便由TypeEvaluator控制。
設置估值器 setEvaluator
類名 | 說明 |
---|---|
IntEvaluator | 這是用於計算 int 屬性的值的默認評估程序。 |
FloatEvaluator | 這是用於計算 float 屬性的值的默認評估程序。 |
ArgbEvaluator | 這是用於計算顏色屬性的值(用十六進制值表示)的默認評估程序。 |
TypeEvaluator | 此接口用於創建您自己的估值器。添加動畫效果的對象屬性不是 int、float 或顏色,那麼就必須實現 TypeEvaluator 接口,才能指定如何計算對象屬性添加動畫效果之後的值。如果想以不同於默認行爲的方式處理 int、float和顏色,還可以自定義 TypeEvaluator。 |
看看其中ArgbEvaluator的小栗子
ValueAnimator colorValueAnimator = ValueAnimator.ofObject(
new ArgbEvaluator(), 0xFF00FF00, 0xFFFF0000, 0xFF0000FF);
colorValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
colorView.setBackgroundColor((Integer)animation.getAnimatedValue());
}
});
colorValueAnimator.setDuration(3000);
colorValueAnimator.start();
自定義TypeEvaluator 中 evaluate函數的幾個參數
- fraction 參數是插值器中的返回值,表示當前動畫的數值進度,百分制的小數點(0.0~1.0)
- startValue 開始值
- endValue 結束值
前面效果的 自定義 的 貝塞爾曲線公式的TypeEvaluator
自定義估值器需要實現 TypeEvaluator的接口 與 複寫evaluate 函數
public class BezierEvaluator implements TypeEvaluator<Point> {
Point controlPoint;
public BezierEvaluator(Point point) {
controlPoint = point;
}
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
float x = (1 - fraction) * (1 - fraction) * startValue.x + 2 * fraction * (1 - fraction) * controlPoint.x + fraction * fraction * endValue.x;
float y = (1 - fraction) * (1 - fraction) * startValue.y + 2 * fraction * (1 - fraction) * controlPoint.y + fraction * fraction * endValue.y;
// 返回對象動畫過渡的邏輯計算後的值
return new Point((int)x, (int)y);
}
}
拋物線的比較常見的效果可以參考 餓了麼等應用的效果
小總結
估值器最要是協助插值器實現 非線性動畫;插值器和估值器都是一個接口,且內部都只有一個方法,我們只要實現接口就可以了,就可以做出很多絢麗的動畫。
一般來說,插值器使用系統的就足夠了,估值器自定義的可能會多一些,另外就是如果要對其他類型(非Int丶float丶color)做動畫,必須自定義類型估值算法。
感興趣的小夥伴的具體的 查詢 官方資料 以及 網上的相關資料
在 XML 中聲明屬性動畫
XML與Animator對應的3個標籤
<animatro />:
對應 ValueAnimator
<animator
android:duration="int" # 動畫執行時間
android:startOffset="int" # 對應代碼 startDelay,延遲多久開始動畫
android:valueFrom="float|int|color" # 起始值
android:valueTo="float|int|color" # 結束值
android:valueType="colorType|floatType|intType|pathType"
android:repeatCount="int" # 對應代碼 setRepeatCount
android:repeatMode="restart(重新從頭開始執行)|reverse(反方向執行)" # 對應代碼 setRepeatMode
android:interpolator="@android:interpolator/xxx" # 對應代碼 setInterpolator,設置插值器
/>
<objectAnimator />:
對應 ObjectAnimator
<objectAnimator
android:propertyName="string" # 屬性名,比如 TranslationX,RotationX,Alpha 等
# 以下的屬性與 animator 一致
android:duration="int"
android:startOffset="int"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:valueType="colorType|floatType|intType|pathType"
android:repeatCount="int|infinite"
android:repeatMode="restart|reverse"
android:interpolator="@android:interpolator/xxx"
/>
<propertyValuesHolder />:
對應 PropertyValuesHolder
<propertyValuesHolder
android:propertyName="string"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:valueType="colorType|floatType|intType|pathType"
/>
<set />:
對應 AnimatorSet
<set
android:ordering="together(同時並行執行)|sequentially(順序依次執行)"
/>
注意需要將XML的動畫文件放在 animator 目錄下
舉個栗子
# 旋轉 + 移動 + 透明 的動畫效果
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
# 旋轉 -720 ~ 0 度 X軸
<objectAnimator
android:duration="3500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="RotationX"
android:valueFrom="-720"
android:valueTo="0"
android:valueType="floatType" />
# 移動 0 ~ 500 X
<objectAnimator
android:duration="3500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="TranslationX"
android:valueFrom="0"
android:valueTo="500"
android:valueType="floatType" />
# 透明 0.1~1.0
<objectAnimator
android:duration="3500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="Alpha"
android:valueFrom="0.1"
android:valueTo="1.0"
android:valueType="floatType" />
</set>
看最終效果
場景小劇場
桌面的 圖標 放大效果
唱盤機的旋轉
歌詞滾動
菜單
音樂控制器升級的手勢動畫效果
存在的問題
1. 動畫再執行,Activity或者Fragment,其它的界面退出了,只能在每個生命週期結束的時候進行清除,很繁瑣!!
2. AnimatorSet無法實現無限循環動畫.(沒有 setRepeatCount 函數). 需要給每個 Animator 動畫去設置 setRepeatCount,很麻煩
3. 無法統一去設置插值器,只能統一寫一個地方,然後調用
4. 弄一些複雜的動畫,代碼寫了N多!!!
其它解決方案:
使用 我們團隊之前寫的 OpenAnim,能很方便的完成以上這些事情,我們團隊現在已經投入使用,並且還支持生命週期的監聽,後續還將支持其它功能(比如 轉場,滾動優化… …)
OpenAnim.with(this)
.into(view)
.together(
new TranslationXAnimator(500),
new TranslationYAnimator(100),
new RotationXAnimator(0, 360),
new RotationYAnimator(0, 360))
.duration(3000)
.interpolate(new LinearInterpolator())
.repeatCount(ValueAnimator.INFINITE)
.start();
# 配置全局配置
<meta-data
android:name="AnimModule"
android:value="com.open.tvwidget.TestAnimModule" />
# 代碼
public class TestAnimModule implements AnimModule {
@Override
public void applyOptions(Context context, AnimConfigBuilder builder) {
// 設置N多種配置(時間,插值器,估值器等等)
builder.duration(1000)
.typeEvaluator(new BounceEaseOut(500))
.interpolate(new DecelerateInterpolator());
}
}
參考資料
屬性動畫文檔地址
屬性動畫XML
Android 動畫 Animator 家族使用指南
估值器詳解
屬性動畫高階用法
Android動畫了解—視圖動畫 <=上個章節 下個章節=> Android動畫了解—轉場/過渡(Transition) 動畫