Android動畫了解—屬性動畫(Property Animation)

屬性動畫

在這裏插入圖片描述
屬性動畫(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等等),也是用這種方式弄出來的.

https://zhuanlan.zhihu.com/p/101794514
感興趣的朋友 參考 官方資料搜索相關資料

在這裏插入圖片描述
資料學習地址

還有一個很酷炫的爆炸粒子效果
在這裏插入圖片描述
相關資料學習地址

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函數(主要是反射+拼裝)

可以改變的控件屬性列表scaleXscaleYtranslationXtranslationYalpharotation 等等。(主要是類裏面可以設置的屬性值,都可以改變,需要有 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的思路,

  1. 每個childView 添加 翻轉 + 透明 到 集合Set.(注意,這裏是同時並行執行動畫效果,使用的playTogether)
  2. 將所有 childView 的 集合set,添加到 總的集合allSet(這裏是順序執行動畫效果,使用的 playSequentially).
  3. 最終的效果就是,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) 動畫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章