Android 你應該知道的動畫

Android中,動畫可以分為三種,分別是補間動畫、幀動畫以及屬性動畫,接下來將對這三種動畫的使用做一個詳細的介紹。

一、補間動畫

補間動畫也稱View動畫,所以它的作用對象只能是View,它有四種典型的變換效果,分別是:平移動畫、縮放動畫、旋轉動畫、透明度動畫。這四種動畫效果可以通過java代碼的方式動態的創建,也可以通過xml文件來創建。
1. 首先看一下通過java代碼的創建方式:
1.1 平移動畫
平移動畫是通過TranslateAnimation類來實現的,常用的構造函數有以下兩個:

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
    }
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
            int fromYType, float fromYValue, int toYType, float toYValue) {
    }

先說第一個構造函數,參數fromXDelta、toXDelta代表x方向平移的起始值和結束值,單位為像素,若toXDelta減fromXDelta大于0,則View右移,否則左移。fromYDelta、toYDelta是同樣的道理,差值大于0View下移,否則上移。
要實現一個View下移100像素可以這么做:

TranslateAnimation translateAnimation = new TranslateAnimation(0f, 0f, 0f, 100f);
translateAnimation.setDuration(2000);//動畫的持續時間,單位毫秒
translateAnimation.setFillAfter(true);//參數為true表示動畫結束后View停留在結束為止
view.startAnimation(translateAnimation);//開始動畫

再看第二個構造函數,在x方向上,fromXType、toXType有三種類型:Animation.ABSOLUTE、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT,分別代表絕對像素、相對于自身平移、相對于父View平移。fromXValue、toXValue,當type為Animation.ABSOLUTE時,這個兩個值為具體的像素值,當type為Animation.RELATIVE_TO_SELF或Animation.RELATIVE_TO_PARENT,這個兩個值為比例值,取值范圍是[0f, 1.0f], y方向上同理。
具體的用法如下:

TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f,
                Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1.0f);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(true);
view.startAnimation(translateAnimation);

此時type都是Animation.RELATIVE_TO_SELF,toYValue的值是-1.0f,即100%,此時則view向上平移自身高度的距離,即就是常見的隱藏title的效果。當type為Animation.RELATIVE_TO_PARENT時,則view向上平移父view高度距離。
1.2 縮放動畫
縮放動畫是通過ScaleAnimation類實現的,常用構造函數如下:

public ScaleAnimation(float fromX, float toX, float fromY, float toY,
            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {
    }

x方向上,參數fromX、toX分別代表view在水平方向縮放的起始比例和結束比例,都是大于等于0的浮點數。pivotXType代表縮放類型,有三種Animation.ABSOLUTE,、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT,pivotXValue代表縮放的中心點,可以是具體的像素值,可以是比比例值,比例值范圍是[0f, 1.0f],比例值是常用的,y方向上同理。
具體用法如下:

ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 2f, 1.0f, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnimation.setDuration(2000);
        scaleAnimation.setFillAfter(true);
        view.startAnimation(scaleAnimation);

實現了view相對于自身中心,在x方向拉伸為原來2倍,在y方向縮小為原來0.5倍。
1.3 旋轉動畫
旋轉動畫是通過RotateAnimation實現的,常用構造函數如下:

public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
            int pivotYType, float pivotYValue) {
    }

參數fromDegrees、toDegrees代表旋轉的開始角度和結束角度,pivotXValue、pivotYValue代表旋轉的中心位置,可以是絕對的像素值,也可以是比例值,比例值范圍是[0f, 1.0f],pivotXType、pivotYType代表旋轉類型,有三種Animation.ABSOLUTE,、Animation.RELATIVE_TO_SELF、Animation.RELATIVE_TO_PARENT
具體用法如下:

RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setDuration(2000);
        rotateAnimation.setFillAfter(true);
        view.startAnimation(rotateAnimation);

實現了view相對自身中心,瞬時間旋轉360度。
1.4 透明度動畫
透明度動畫通過AlphaAnimation類實現,構造函數如下:

public AlphaAnimation(float fromAlpha, float toAlpha) {
    }

參數fromAlpha、toAlpha代表透明度的起始值和結束值,0f代表完全透明,1.0f則無透明度。
具體用法如下:

AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        alphaAnimation.setDuration(2000);
        alphaAnimation.setFillAfter(true);
        view.startAnimation(alphaAnimation);

實現了view從無透明度到完全透明的變化。
1.5 View的組合動畫
View的組合動畫通過AnimationSet類實現的,具體用法如下:

AnimationSet animationSet = new AnimationSet(true);
        
        RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        
        animationSet.addAnimation(rotateAnimation);
        animationSet.addAnimation(alphaAnimation);
        animationSet.setFillAfter(true);
        animationSet.setDuration(2000);
        view.startAnimation(animationSet);

AnimationSet的參數為true表示組合動畫公用一個插值器,什么是插值器呢?就是動畫速度的變化規律,常用的插值器如下:
LinearInterpolator:勻速
AccelerateInterpolator:加速
AccelerateDecelerateInterpolator:先加速再減速
DecelerateInterpolator:減速
BounceInterpolator:阻尼下落,即反彈數次后停止

可通過如下方式使用

RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setDuration(2000);
        rotateAnimation.setFillAfter(true);

        rotateAnimation.setInterpolator(new AccelerateInterpolator());

        view.startAnimation(rotateAnimation);

最后,還可以通過setRepeatCount()、setRepeatMode()來設置動畫重復的次數、和重復模式,重復模式包括Animation.RESTART、Animation.REVERSE,即重新開始和逆序播放。
如果要監聽動畫的執行情況,則可以通過如下接口:

public static interface AnimationListener {
        //開始
        void onAnimationStart(Animation animation);
        //結束
        void onAnimationEnd(Animation animation);
        //重復
        void onAnimationRepeat(Animation animation);
    }

2. xml方式實現
xml文件需要放到res目錄下的anim文件夾。
2.1 平移動畫
通過<translate >標簽實現。
實現View向上平移自身高度距離,即title隱藏效果:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:fromXDelta="0%"
    android:fromYDelta="0%"
    android:toXDelta="0%"
    android:toYDelta="-100%" />

實現View向上平移50像素

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="0"
    android:toYDelta="-50" />

2.2 縮放動畫
通過<scale >標簽實現。
實現View相對于自身中心,在x方向拉伸為原來2倍,在y方向縮小為原來0.5倍:

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="2.0"
    android:toYScale="0.5" />

2.3 旋轉動畫
通過<scale >標簽實現。
實現View相對自身中心,瞬時間旋轉360度,同時逆序重復兩次:

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="2"
    android:repeatMode="reverse"
    android:toDegrees="360" />

2.4 透明度動畫
通過<alpha>標簽實現。
實現了View透明度從1.0f到0f的變化,同時是加速變化的:

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0" />

2.5 View的組合動畫
通過<set>標簽實現,但不能控制次序,只能同時發生,測試中發現,此時repeatCount屬相無效。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:fillAfter="true"
    android:interpolator="@android:anim/linear_interpolator"
    android:shareInterpolator="true">
    <alpha
        android:fromAlpha="1.0"
        android:toAlpha="0" />

    <rotate
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="360" />

    <scale
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.5"
        android:toYScale="0.5" />

    <translate
        android:fromXDelta="0%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="-100%" />
</set>

通過xml方式實現時,需要先通過loadAnimation()加載xml文件,如下:

Animation animation = AnimationUtils.loadAnimation(context, R.anim.set_anim);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }
            @Override
            public void onAnimationEnd(Animation animation) {
            }
            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        view.startAnimation(animation);

補間動畫在Android最初的版本就有了,在功能和可擴展方面都有相當大的局限性,例如平移效果只能改變View的顯示效果而已,并不能改變View真正的位置,舉個例子,如果將一個有點擊事件的Button從屏幕左上角移動到屏幕右上角,點擊右上角按鈕,發現并不能響應點擊事件,此時再點擊屏幕左上角竟然有響應。還有補間動畫只能作用于View,如果我們要對一個費View的對象進行動畫操作,那就無能為力了,正式因為種種功能上的缺陷,Android在3.0版本中引入了屬性動畫來進一步完善Android的動畫機制。

二、屬性動畫

有了屬性動畫,我們除了最基本的對View進行平移、縮放、旋轉、透明度操作外,還可以將動畫作用于指定的對象上,例如將一個Point對象從(0, 0)位置移動到(100, 100)位置。但是呢,有一點要注意,屬性動畫只能只能在Android3.0即以上版本使用,如果要兼容Android3.0以下版本,可以考慮使用大神JakeWharton的動畫庫:http://nineoldandroids.com/,但是這個庫在Android3.0以下的屬性動畫其實還是傳統的補間動畫哦!
屬性動畫中核心的兩個類就是ValueAnimator和ObjectAnimator,我們來了一個個看。

1.ValueAnimator是用來計算動畫對應屬性初始值和結束值之間的過渡的,類似于一個數值發生器,例如我們要實現數值0到數值2再到數值0在2000毫秒的過渡,可以這樣做:

ValueAnimator animator = ValueAnimator.ofFloat(0f, 2f, 0f); 
//設置監聽器
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float curValue = (float) animation.getAnimatedValue();//當前值
                float fraction = animation.getAnimatedFraction();//當前已過渡完成的比例
            }
        });
        animator.setDuration(2000);//動畫時長
        animator.start();

ValueAnimator.ofFloat()是實現浮點數的平滑過渡,如果需要整數的平滑過渡則可以使用ValueAnimator.ofInt(),用法基本一致。除了這兩個外還有 ValueAnimator.ofArgb()、 ValueAnimator.ofObject(),其中ValueAnimator.ofArgb()可以用來進行顏色值的過渡,ValueAnimator.ofObject()可以用來實現對象的過渡效果,這是就需要我們自行定義擴展了。
ValueAnimator.ofFloat()是如何實現過渡效果的呢?其實就是通過一個FloatEvaluator來完成的,不斷的計算當前的值:

public class FloatEvaluator implements TypeEvaluator {  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        float startFloat = ((Number) startValue).floatValue();  
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
    }  
}  

如果我們要從點Point(0, 0)過渡到點Point(100, 100),同樣也需要實現一個TypeEvaluator來計算當前的過渡屬性值:

public class PointEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint.x + fraction * (endPoint.x - startPoint.x);
        float y = startPoint.y + fraction * (endPoint.y - startPoint.y);
        return new Point((int) x, (int) y);
    }
}

很簡單,fraction代表已經過渡完成的比例,根據當前完成的比例、開始點和結束點計算出當前點的值。有了PointEvaluator就可以實現我們自己的Point過渡效果了:

Point startPoint = new Point(0, 0);
Point endPoint = new Point(100, 100);
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point currPoint = (Point) animation.getAnimatedValue();
            }
        });

這樣就通過TypeEvaluator實現了一個我們自定義的估值器。
2.ValueAnimator只是對值進行了一個平滑的動畫過渡,ObjectAnimator才是實現對任意對象的屬性進行動畫操作的,同是ObjectAnimator是ValueAnimator的子類。先看一下如何通過ObjectAnimator實現傳統補間動畫的四種效果:
2.1 平移動畫
要將一個View右移出屏幕,再移動回來,可以這樣做:

float translationX = view.getTranslationX();
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", translationX, 600f, translationX);
animator.setDuration(3000);
animator.start();

通過ofFloat()方法我們創建了一個ObjectAnimator對象,ofFloat()方法的第一個參數view就是要進行平移操作的對象,因為我們要對view進行平移操作,所以第二個參數傳入translationX,代表view的平移屬性,之后的參數是一個是可變長度的,個數根據你的需求控制。所以核心的參數就是第二個,根據這個屬性類型來區分對View進行何種動畫操作。
2.2 縮放動畫
同樣的道理,實現View的縮放效果可以這樣做:

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f, 1f);
animator.setDuration(5000);
animator.start();

我們將ofFloat()方法第二個參數換成了scaleX,表示在x方向對view進行縮放操作,這樣我們就實現了view拉伸兩倍再還原的效果。
2.3 旋轉動畫
例如,要將一個View旋轉360度可以這樣做,只要將ofFloat()第二個參數寫成rotation,起始、結束角度分別為0和360:

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
animator.setDuration(3000);
animator.start();

2.4 透明度動畫
只要將ofFloat()第二個參數寫成alpha,則實現了View透明度從1到0在到0的變化:

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);
animator.setDuration(5000);
animator.start();

2.5 顏色動畫
使用屬性動畫改變一個View的背景顏色也是可以的,如下代碼可以實現View背景色從藍到紅的變化:

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "backgroundColor", Color.BLUE, Color.RED);
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(5000);
animator.start();

2.6 組合動畫
和補間動畫類似,屬性動畫同樣可以將單個動畫進行組合,而且功能更強大,需要通過AnimatorSet類來實現,通過調用其play()方法得到一個AnimatorSet.Builder對象,Builder對象有一下四個方法:
after(Animator anim) 將現有動畫插入到傳入的動畫之后執行
after(long delay) 將現有動畫延遲指定毫秒后執行
before(Animator anim) 將現有動畫插入到傳入的動畫之前執行
with(Animator anim) 將現有動畫和傳入的動畫同時執行

我們要實現一個View從屏幕右側移入屏幕,然后旋轉360度同時有透明度變化,最后在水平方向拉伸兩倍后還原的效果可以這樣么做:

AnimatorSet animatorSet = new AnimatorSet();

ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f, 1f);
ObjectAnimator rotation = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
ObjectAnimator translation = ObjectAnimator.ofFloat(view, "translationX", 600f, 0f);
ObjectAnimator scale = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f, 1f);

animatorSet.play(alpha).with(rotation).after(translation).before(scale);
animatorSet.setDuration(5000);
animatorSet.start();

當然還可以setRepeatCount()、setRepeatMode()設置動畫的重復次數以及重復模式,重復模式有ValueAnimator.RESTART、ValueAnimator.REVERSE兩種。
3.除了通過代碼來編寫屬性動畫外,還可以使用xml的方式,xml文件需要放到res目錄下的animator文件夾。可用的標簽有以下三種:

  • <animator> 代表ValueAnimator
  • <objectAnimator> 代表ObjectAnimator
  • <set> 代表AnimatorSet
    好了,看幾個例子:
    要實現0到50平滑過渡的效果,可以這么做:
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:valueFrom="0"
    android:valueTo="50"
    android:valueType="floatType" />

要將一個View旋轉360度,可以這么做:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360"
    android:valueType="floatType" />

要將一個View透明度從1變為0,可以這樣編寫:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:propertyName="alpha"
    android:valueFrom="1"
    android:valueTo="0"
    android:valueType="floatType" />

平移和縮放動畫都是類似的。再看一下<set>標簽的用法:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="1500"
        android:propertyName="translationX"
        android:valueFrom="-500"
        android:valueTo="0"
        android:valueType="floatType" />

    <set android:ordering="together">
        <objectAnimator
            android:duration="2000"
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType" />

        <set android:ordering="sequentially">
            <objectAnimator
                android:duration="1000"
                android:propertyName="alpha"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType" />
            <objectAnimator
                android:duration="1000"
                android:propertyName="alpha"
                android:valueFrom="0"
                android:valueTo="1"
                android:valueType="floatType" />
        </set>
    </set>

    <set android:ordering="together">
        <objectAnimator
            android:duration="1500"
            android:propertyName="scaleX"
            android:valueFrom="1"
            android:valueTo="2"
            android:valueType="floatType" />

        <objectAnimator
            android:duration="1500"
            android:propertyName="scaleX"
            android:valueFrom="2"
            android:valueTo="1"
            android:valueType="floatType" />
    </set>
</set>

我們實現了View先平移,然后同時進行旋轉和透明度變化,最后進行縮放的動畫效果。其中ordering屬相我們使用了sequentially、together兩種,分別代表順序播放和同時播放。還有以下兩個我們沒用到的屬性:startOffset:表示動畫的延時啟動時間,以及repeatCount、repeatMode
使用xml動畫文件也是非常簡單的:

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

4.Animator類當中提供了一個addListener()方法,可以用來監聽動畫的執行情況,無論ObjectAnimator、ValueAnimator還是AnimatorSet都是Animator的子類,所以它們都可以使用addListener():

anim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });

如果想監聽其中的某些事件則可以通過AnimatorListenerAdapter來實現:

animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
            }
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });

這樣我們只監聽了動畫的開始和結束事件。

5. 屬性動畫的插值器是兼容補間動畫的插值器的,所以補間動畫中的插值器完全可以在屬性動畫中使用。另外屬性動畫提供了一個TimeInterpolator接口,它的作用是根據時間流逝的百分比計算出當前屬性值改變的百分比,通過這個接口我們來自定義屬性動畫插值器:

public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

接口中只有一個getInterpolation()方法,其中input參數會根據動畫設置的時長在0到1之間勻速增長的變化。如果要自定義插值器可以這樣寫:

public class MyInterpolator implements TimeInterpolator {
    @Override
    public float getInterpolation(float input) {
        float result = 0;
        //todo 
        return result;
    }
}

考驗數學功底的時候來了。。。。具體的實現細節可參考系統插值器。有一點需要注意,我們計算出來的result的值必須在0到1之間哦。

6. 回顧一下,我們在java代碼中可以通過ObjectAnimator.ofFloat()ObjectAnimator.ofInt()來實現屬性動畫其中第二個參數可以是alpha、rotation、scaleX、translateX等等。為什么第二個參數可以是這些呢?這是因為ObjectAnimator內部的工作機制并不是對傳入的屬性名進行操作的,而是根據屬性名在當前子View類以及父類中去找對應的get和set方法,然后通過方法不斷地對值進行改變,從而實現動畫效果的,例如我們可以在View類中找到了參數rotation對應的get和set方法:

public float getRotation() {
        return mRenderNode.getRotation();
    }

public void setRotation(float rotation) {
        if (rotation != getRotation()) {
            // Double-invalidation is necessary to capture view's old and new areas
            invalidateViewProperty(true, false);
            mRenderNode.setRotation(rotation);
            invalidateViewProperty(false, true);

            invalidateParentIfNeededAndWasQuickRejected();
            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
    }

既然如此,除了系統提供的屬性動畫外,如果要給一個自定義Button添加一個widths屬相動畫,實現其寬度的變化,如果使用translateX屬性會導致Button內容的拉伸,這并不是我們愿意看到的,所以我們自定義的widths屬相動畫并沒有這種問題。首先看我們自定義的Button類:

public class MyButton extends Button{
    public MyButton(Context context) {
        super(context);
    }

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public int getWidths(){
        return getLayoutParams().width;
    }

    public void setWidths(int width){
        getLayoutParams().width = width;
        requestLayout();
    }
}

因為我們規定屬性名為widths,所以我們提供了getWidths()、setWidths()兩個方法,當然這兩個方法也是必須的。接下來還需要編寫一個TypeEvaluator類來告訴系統寬度如何過渡:

public class WidthsEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        int startWidth = (int) startValue;
        int endWidth = (int) endValue;
        return (int)(startWidth + fraction * (endWidth - startWidth));
    }

有了WidthsEvaluator類,我們需要的東西也就夠了,看下布局文件、以及使用方法:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.othershe.mybutton.MyButton
        android:id="@+id/my_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="哎呦,不錯哦!" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="start"
        android:text="開始" />
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
    private MyButton button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (MyButton) findViewById(R.id.my_btn);

    }

    public void start(View view) {
        int width = button.getWidth();
        ObjectAnimator animator = ObjectAnimator.ofObject(button, "widths", new WidthsEvaluator(), width, 600);
        animator.setDuration(3000);
        animator.start();
    }
}

通過ObjectAnimator.ofObject()來調用的,很簡單,看下效果:

MyButton

7. 通過java代碼實現屬性動畫除了通過ObjectAnimator類,還有另外一種方式,就是使用ViewPropertyAnimator類。例如我們要實現一個球形View自由落體的效果,可以這樣寫:

view.animate().x(0).y(500)
    .setDuration(5000)  
    .setInterpolator(new BounceInterpolator());  

通過view.animate()方法得到一個ViewPropertyAnimator對象,之后的操作都是基于該對象的方法,而且是鏈式調用的,同時在鏈尾系統會默認的添加start()方法,所以動畫會自動執行。僅僅是寫法的不同,根據喜好選擇吧,其它的方法有興趣的話可以自行測試。

三、幀動畫

幀動畫是順序的播放一系列圖片,從而產生動畫的效果,其實也就是圖片的切換。但是如果圖片過多、過大的話是很容易產生OOM的,所以使用時需要注意。
首先在res目錄下的drawable文件夾編寫一個xml文件:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">//
    <item android:drawable="@mipmap/icon1" android:duration="300"/>
    <item android:drawable="@mipmap/icon2" android:duration="300"/>
    <item android:drawable="@mipmap/icon3" android:duration="300"/>
    <item android:drawable="@mipmap/icon4" android:duration="300"/>
    <item android:drawable="@mipmap/icon5" android:duration="300"/>
    <item android:drawable="@mipmap/icon6" android:duration="300"/>
    <item android:drawable="@mipmap/icon7" android:duration="300"/>
    <item android:drawable="@mipmap/icon8" android:duration="300"/>
    <item android:drawable="@mipmap/icon9" android:duration="300"/>
</animation-list>

oneshot屬性表示是否循環播放,值為true則只播放一次。

通如下方法調用,其中view是一個ImageView對象:

view.setImageResource(R.drawable.icons);
AnimationDrawable animationDrawable = (AnimationDrawable) view.getDrawable();
animationDrawable.start();

如果要停止播放可通過如下方法:

AnimationDrawable animationDrawable = (AnimationDrawable) view.getDrawable();
animationDrawable.stop();

到這里Android動畫相關的內容就結束了,足以應對開發中的使用場景了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容