Android動畫之屬性動畫

概述

Android屬性動畫是Android 3.0之后添加的一個(gè)非常強(qiáng)大的動畫。與視圖動畫不同的地方在于,屬性動畫能夠真正的改變View的屬性。例如:如果我們通過視圖動畫移動了一個(gè)View,那么這個(gè)View真正的位置并沒有發(fā)生改變,它的點(diǎn)擊事件依然保留在原來的位置。而如果我們通過屬性動畫改變了一個(gè)View的位置,那么它真實(shí)的位置也就改變了。此外屬性動畫并不局限于對View實(shí)現(xiàn)動畫,它幾乎可以對任何一個(gè)對象的任何屬性實(shí)現(xiàn)動畫。

Animator三個(gè)子類

通常我們實(shí)現(xiàn)屬性動畫并不需要直接使用Animator,而是使用它的以下三個(gè)子類:

  • ObjectAnimator
  • ValueAnimator
  • AnimatorSet

ObjectAnimator

一般我通過ObjectAnimator的工廠方法ofFloat()來獲得它的實(shí)例(其他的方法還有ofInt()ofArgb())。

例如下代碼實(shí)現(xiàn)了一個(gè)位移動畫:

ObjectAnimator translate = ObjectAnimator.ofFloat(view, "translationX", 200f);
translate.setDuration(1000);  
translate.start();
ofFloat()參數(shù)說明
  1. 第一參數(shù)是一個(gè)需要實(shí)現(xiàn)動畫的View。
  2. 第二個(gè)參數(shù)是需要操作的屬性。
  3. 最后一個(gè)其實(shí)是可變參數(shù)。用來表示屬性值的一系列取值。當(dāng)只傳一個(gè)值的時(shí)候,表示從初始值,變化到該值。
常用屬性值
  • translationXtranslationY: 控制View距離左邊和頂部的距離的增加值。是一個(gè)相對值。
  • rotation、rotationXrotationY: rotation是控制View圍繞其支點(diǎn)進(jìn)行旋轉(zhuǎn)。rotationXrotationY分別是圍繞X軸和Y軸旋轉(zhuǎn)。
  • scaleXscaleY: 控制View的縮放。
  • pivotXpivotY: 控制View的支點(diǎn)位置,進(jìn)行旋轉(zhuǎn)和縮放,默認(rèn)是View的中點(diǎn)。它們都是float值,0表示View的最左邊和最頂端,1表示最右端和最下端。
  • alpha: 控制View的透明度。
  • xy: 控制View在布局容器中距離左邊和頂部的距離。是一個(gè)絕對值。

注意

當(dāng)用屬性動畫操作一個(gè)View的屬性時(shí),這個(gè)View必須具有相應(yīng)的get和set方法。

ValueAnimator

ValueAnimatorObjectAnimator的父類。它有點(diǎn)像一個(gè)數(shù)值發(fā)生器。不提供任何動畫效果。通常我們需要在onAnimationUpdate獲取當(dāng)前時(shí)間點(diǎn)發(fā)生的值,然后操作對象的屬性,以實(shí)現(xiàn)動畫效果。

如下代碼實(shí)現(xiàn)了一個(gè)倒計(jì)時(shí)效果:

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();

補(bǔ)充
系統(tǒng)大約每10ms刷新一次,來更新屬性值。所以如果我們要做一個(gè)以秒為單位的計(jì)時(shí)器,可以直接用這種效果實(shí)現(xiàn)。

AnimatorSet

如果我們要對一個(gè)對象的多個(gè)屬性實(shí)現(xiàn)動畫,通常有如下兩種做法:

  • 使用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();

補(bǔ)充
AnimatorSet可以通過playSequentially(),playTogether(),來控制動畫的串行和并行。通過set.play().with()、before()、after()等方法可以實(shí)現(xiàn)動畫的多種協(xié)同效果。

操作一個(gè)沒有set和get的屬性

上文已經(jīng)提到當(dāng)用屬性動畫操作一個(gè)View的屬性時(shí),這個(gè)View必須具有相應(yīng)的get和set方法。那么如果我們想操作一個(gè)沒有g(shù)et和set的屬性,該怎么辦呢?
通常有三種實(shí)現(xiàn)方式:

  • 自己創(chuàng)建一個(gè)屬性類。
  • 通過一個(gè)包裝類來實(shí)現(xiàn)。
  • 通過ValueAnimator來實(shí)現(xiàn)。

通過包裝類實(shí)現(xiàn)

當(dāng)我們查看View的set和get方法時(shí),它只有scaleX和scaleY,而沒有scale屬性。有時(shí)候,我們需要對一個(gè)View的X和Y都進(jìn)行縮放,又不想使用兩次ObjectAnimator,就可以通過如下方法實(shí)現(xiàn)。

如下代碼是一個(gè)包裝類的示例:

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;
    }
}

使用該包裝類直接實(shí)現(xiàn)對View的縮放:

WrapperView target = new WrapperView(view);
ObjectAnimator scale = ObjectAnimator.ofFloat(target,"scale",1f,2f);
scale.setDuration(1000);  
scale.start();

通過ValueAnimator實(shí)現(xiàn)

如果我們想實(shí)現(xiàn)一個(gè)View在一個(gè)曲線運(yùn)動的動畫,就可以通過如下方法實(shí)現(xiàn)。
(曲線公式為: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類似,設(shè)置屬性的起始值和結(jié)束值。
animator.setObjectValues(new PointF(offsetX, offsetY), new PointF(TRANS_X + offsetX, offsetY));
// 設(shè)置自定義的Evaluator.
animator.setEvaluator(new TypeEvaluator<PointF>() {
    @Override
    // api level 21以上已經(jīng)實(shí)現(xiàn)了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;
    }
});
// 通過監(jiān)聽器得到相應(yīng)的Evaluator值,并應(yīng)用。
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();

說明
通常如果我們需要操作一個(gè)不存在的屬性,我們需要自定義一個(gè)Evaluator。通過這個(gè)Evaluator在動畫運(yùn)行的過程中生成相應(yīng)的值。然后給ValueAnimator設(shè)置一個(gè)監(jiān)聽器,通過getAnimatedValue()來得到相應(yīng)的值。然后根據(jù)這個(gè)值,做相應(yīng)的操作。

關(guān)于Evaluator

Evaluator翻譯過來是估值器的意思,實(shí)際上它是一種計(jì)算屬性值的算法。

  • 系統(tǒng)定義的Evaluator有ArgbEvaluator, FloatEvaluator, IntEvaluator, PointFEvaluator等。
  • 我們自定義Evaluator只需要實(shí)現(xiàn)TypeEvaluator接口,并重寫T evaluate(float fraction,T startValue,T endValue)方法即可。其中fraction是根據(jù)設(shè)置的Interpolator生成的(系統(tǒng)默認(rèn)的Interpolator是AccelerateDecelerateInterpolator,即先加速后減速。),它表示變化的百分比(0表示0%,1表示100%)。例如:如果我們將一個(gè)View從(0,0)移動到(0,100),duration是1000ms,Interpolator為LinearInterpolator。那么在動畫執(zhí)行到500ms時(shí),這個(gè)fraction就等于0.5。

在xml中使用屬性動畫

屬性動畫的保存在res/animator文件夾下。
如下代碼定義了一個(gè)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代碼中調(diào)用

Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator);
animator.setTarget(view);
animator.start();

通過KeyFrame定義動畫

// 這里ofFloat(),的第一個(gè)參數(shù)表示動畫完成的百分比。
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();

動畫事件的監(jiān)聽

我們可以使用AnimatorListen接口來監(jiān)聽動畫的Start、Repeat、End、Cancel。但是這樣做會過于繁瑣,有時(shí)候我們只想監(jiān)聽動畫的部分事件,這時(shí)候我們可以使用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();  

注: 本文主要參考有:網(wǎng)易微專業(yè)課程、Android群英傳。當(dāng)然還有Api guide。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容