概述
Android屬性動畫是Android 3.0之后添加的一個非常強大的動畫。與視圖動畫不同的地方在于,屬性動畫能夠真正的改變View的屬性。例如:如果我們通過視圖動畫移動了一個View,那么這個View真正的位置并沒有發生改變,它的點擊事件依然保留在原來的位置。而如果我們通過屬性動畫改變了一個View的位置,那么它真實的位置也就改變了。此外屬性動畫并不局限于對View實現動畫,它幾乎可以對任何一個對象的任何屬性實現動畫。
Animator三個子類
通常我們實現屬性動畫并不需要直接使用Animator
,而是使用它的以下三個子類:
- ObjectAnimator
- ValueAnimator
- AnimatorSet
ObjectAnimator
一般我通過ObjectAnimator的工廠方法ofFloat()
來獲得它的實例(其他的方法還有ofInt()
和ofArgb()
)。
例如下代碼實現了一個位移動畫:
ObjectAnimator translate = ObjectAnimator.ofFloat(view, "translationX", 200f);
translate.setDuration(1000);
translate.start();
ofFloat()
參數說明
- 第一參數是一個需要實現動畫的View。
- 第二個參數是需要操作的屬性。
- 最后一個其實是可變參數。用來表示屬性值的一系列取值。當只傳一個值的時候,表示從初始值,變化到該值。
常用屬性值
-
translationX
和translationY
: 控制View距離左邊和頂部的距離的增加值。是一個相對值。 -
rotation
、rotationX
和rotationY
:rotation
是控制View圍繞其支點進行旋轉。rotationX
和rotationY
分別是圍繞X軸和Y軸旋轉。 -
scaleX
和scaleY
: 控制View的縮放。 -
pivotX
和pivotY
: 控制View的支點位置,進行旋轉和縮放,默認是View的中點。它們都是float
值,0
表示View的最左邊和最頂端,1
表示最右端和最下端。 -
alpha
: 控制View的透明度。 -
x
和y
: 控制View在布局容器中距離左邊和頂部的距離。是一個絕對值。
注意
當用屬性動畫操作一個View的屬性時,這個View必須具有相應的get和set方法。
ValueAnimator
ValueAnimator
是ObjectAnimator
的父類。它有點像一個數值發生器。不提供任何動畫效果。通常我們需要在onAnimationUpdate
獲取當前時間點發生的值,然后操作對象的屬性,以實現動畫效果。
如下代碼實現了一個倒計時效果:
ValueAnimator countDown = ValueAnimator.ofInt(10, 0);
countDown.setDuration(10000);
countDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
textView.setText("" + animation.getAnimatedValue());
}
});
countDown.start();
補充
系統大約每10ms刷新一次,來更新屬性值。所以如果我們要做一個以秒為單位的計時器,可以直接用這種效果實現。
AnimatorSet
如果我們要對一個對象的多個屬性實現動畫,通常有如下兩種做法:
- 使用PropertyValuesHolder
- 使用AnimatorSet
使用PropertyValuesHolder
PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.0f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.0f);
ObjectAnimator together = ObjectAnimator.ofPropertyValuesHolder(view, rotate, scaleX, scaleY);
together.setDuration(1000);
together.start();
使用AnimatorSet
ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f).setDuration(1000);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f).setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, 2.0f).setDuration(1000);
AnimatorSet set = new AnimatorSet();
set.playSequentially(rotate, scaleX, scaleY);
set.setDuration(1000);
set.start();
補充
AnimatorSet可以通過playSequentially(),playTogether(),來控制動畫的串行和并行。通過set.play().with()、before()、after()等方法可以實現動畫的多種協同效果。
操作一個沒有set和get的屬性
上文已經提到當用屬性動畫操作一個View的屬性時,這個View必須具有相應的get和set方法。那么如果我們想操作一個沒有get和set的屬性,該怎么辦呢?
通常有三種實現方式:
- 自己創建一個屬性類。
- 通過一個包裝類來實現。
- 通過ValueAnimator來實現。
通過包裝類實現
當我們查看View的set和get方法時,它只有scaleX和scaleY,而沒有scale屬性。有時候,我們需要對一個View的X和Y都進行縮放,又不想使用兩次ObjectAnimator,就可以通過如下方法實現。
如下代碼是一個包裝類的示例:
public class WrapperView {
private View target;
private float scale;
public WrapperView(View target){
this.target = target;
}
public setScale(float scale){
this.scale = scale;
target.setScaleX = scale;
target.setScaleY = scale;
}
public float getScale(){
return scale;
}
}
使用該包裝類直接實現對View的縮放:
WrapperView target = new WrapperView(view);
ObjectAnimator scale = ObjectAnimator.ofFloat(target,"scale",1f,2f);
scale.setDuration(1000);
scale.start();
通過ValueAnimator實現
如果我們想實現一個View在一個曲線運動的動畫,就可以通過如下方法實現。
(曲線公式為:y=-1/150x^2+4x )
public static final int TRANS_X = 600;
ValueAnimator animator = new ValueAnimator();
float offsetX = view.getX();
float offsetY = view.getY();
animator.setDuration(2000);
// 和ofFloat類似,設置屬性的起始值和結束值。
animator.setObjectValues(new PointF(offsetX, offsetY), new PointF(TRANS_X + offsetX, offsetY));
// 設置自定義的Evaluator.
animator.setEvaluator(new TypeEvaluator<PointF>() {
@Override
// api level 21以上已經實現了PointFEvalutor.
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF = new PointF();
float d = fraction * TRANS_X;
pointF.x = startValue.x + d;
pointF.y = startValue.y + (1.0f / 150f) * d * d - 4 * d;
return pointF;
}
});
// 通過監聽器得到相應的Evaluator值,并應用。
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
view.setX(pointF.x);
view.setY(pointF.y);
}
});
animator.start();
說明
通常如果我們需要操作一個不存在的屬性,我們需要自定義一個Evaluator
。通過這個Evaluator
在動畫運行的過程中生成相應的值。然后給ValueAnimator
設置一個監聽器,通過getAnimatedValue()
來得到相應的值。然后根據這個值,做相應的操作。
關于Evaluator
Evaluator
翻譯過來是估值器的意思,實際上它是一種計算屬性值的算法。
- 系統定義的
Evaluator
有ArgbEvaluator, FloatEvaluator, IntEvaluator, PointFEvaluator等。 - 我們自定義
Evaluator
只需要實現TypeEvaluator
接口,并重寫T evaluate(float fraction,T startValue,T endValue)
方法即可。其中fraction
是根據設置的Interpolator
生成的(系統默認的Interpolator是AccelerateDecelerateInterpolator
,即先加速后減速。),它表示變化的百分比(0表示0%,1表示100%)。例如:如果我們將一個View從(0,0)移動到(0,100),duration
是1000ms,Interpolator為LinearInterpolator
。那么在動畫執行到500ms時,這個fraction
就等于0.5。
在xml中使用屬性動畫
屬性動畫的保存在res/animator文件夾下。
如下代碼定義了一個AnimatorSet:
xml文件
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<objectAnimator
android:duration="2000"
android:propertyName="translationY"
android:valueTo="-200f"
android:valueType="floatType"/>
<set android:ordering="together">
<objectAnimator
android:duration="2000"
android:propertyName="scaleX"
android:valueTo="2.0f"
android:valueType="floatType"/>
<objectAnimator
android:duration="2000"
android:propertyName="scaleY"
android:valueTo="2.0f"
android:valueType="floatType"/>
</set>
</set>
在Java代碼中調用
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator);
animator.setTarget(view);
animator.start();
通過KeyFrame定義動畫
// 這里ofFloat(),的第一個參數表示動畫完成的百分比。
Keyframe keyframe1 = Keyframe.ofFloat(0f, 0f);
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 360f);
Keyframe keyframe3 = Keyframe.ofFloat(1.0f, 0f);
PropertyValuesHolder pvh = PropertyValuesHolder.ofKeyframe("rotation", keyframe1, keyframe2, keyframe3);
ObjectAnimator rotate = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
rotate.setDuration(2000);
rotate.start();
動畫事件的監聽
我們可以使用AnimatorListen
接口來監聽動畫的Start、Repeat、End、Cancel。但是這樣做會過于繁瑣,有時候我們只想監聽動畫的部分事件,這時候我們可以使用AnimatorListenAdapter
接口。
直接使用View的animate方法
view.animate()
.alpha(0)
.y(300)
.setDuration(1000)
.withStartAction(new Runnable(){
@Override
public void run(){
}
})
.withEndAction(new Runnable(){
@Override
public void run(){
runOnUiThread(new Runnable(){
@Override
public void run(){
}
});
}
}).start();
注: 本文主要參考有:網易微專業課程、Android群英傳。當然還有Api guide。