banner
jzman

jzman

Coding、思考、自觉。
github

Android屬性動畫

上一篇 總結了 Android 幀動畫和補間動畫的相關知識,這篇文章將介紹屬性動畫,屬性動畫相較幀動畫和補間動畫更強大,幀動畫和補間動畫只能應用於 View 及其子類,而屬性動畫可以修改任何對象的屬性值,屬性值可在指定的一段時間內自動改變,根據對象屬性值的變化進而實現更複雜的動畫。

  1. 屬性動畫的常用設置
  2. ValueAnimator
  3. ObjectAnimator
  4. 關鍵幀
  5. 插值器和估值器

屬性動畫的常用設置#

下面是屬性動畫的常用設置,具體如下:

//設置屬性動畫持續時間
animator.setDuration(2000);
//設置屬性插值器
animator.setInterpolator(new AccelerateInterpolator());
//設置屬性動畫重複播放模式
animator.setRepeatMode(ValueAnimator.REVERSE);
//設置屬性動畫重複播放次數
animator.setRepeatCount(0);
//設置屬性動畫延遲播放的時間
animator.setStartDelay(0);
//設置屬性動畫估值器,用於控制最終屬性值(API22)
animator.setCurrentFraction(0.5f);
//設置當前播放時間,其值在Duration範圍之內
animator.setCurrentPlayTime(1000);
//設置屬性動畫估值器,用於控制最終屬性值
animator.setEvaluator(new IntEvaluator());
//設置屬性動畫監聽
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Log.i(TAG, animation.getAnimatedValue() + "");
        //
    }
});
//...

ValueAnimator#

ValueAnimator 提供了一個簡單的計時引擎,用於執行動畫時根據設置的時長以及其他屬性完成動畫值的計算,然後就可以將動畫值設置到合適的目標對象上,使用的插值器默認是 AccelerateDecelerateInterpolator,表示動畫開始和結束時較慢,中間加速完成動畫,下面是源碼中默認的插值器,具體如下:

// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
        new AccelerateDecelerateInterpolator();

在 ValueAnimator 中已經內部處理了一些估值器 IntEvaluator 和 FloatEvaluator,也就是說如果使用的時 ofInt 和 ofFloat 方法作為動畫的屬性值,那麼 ValueAnimator 會自動處理 int 和 float 值的變化,在源碼中找了一下,這部分內容在 PropertyValuesHolder 這個類中,具體如下:

void init() {
    if (mEvaluator == null) {
        // We already handle int and float automatically, but not their Object
        // equivalents
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator :
                null;
    }
    if (mEvaluator != null) {
        // KeyframeSet knows how to evaluate the common types - only give it a custom
        // evaluator if one has been set on this class
        mKeyframes.setEvaluator(mEvaluator);
    }
}

ValueAnimator 可以使用代碼創建,也可以使用 xml 創建,下面以平移動畫為例說明 ValueAnimator 的使用方式,其他如縮放、旋轉等使用方式類似。

使用代碼創建#

private void translation(){
    ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
    valueAnimator.setDuration(2000);
    valueAnimator.setInterpolator(new AccelerateInterpolator());
    valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
    valueAnimator.setRepeatCount(0);
    valueAnimator.setStartDelay(0);
//    valueAnimator.setCurrentFraction(0.5f);
//    valueAnimator.setCurrentPlayTime(1000);
    valueAnimator.setEvaluator(new IntEvaluator());

    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Log.i(TAG, animation.getAnimatedValue() + "");
            int x = (int) animation.getAnimatedValue();
            ivImage.setTranslationX(x);
            ivImage.setTranslationY(x);
        }
    });

    valueAnimator.start();
}

使用 xml 創建#

在 res/animator 文件夾下創建 test_animator.xml 文件,文件內容如下:

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0"
    android:valueTo="100"
    android:valueType="intType"

    android:duration="2000"
    android:startOffset ="0"
    android:repeatMode = "reverse"
    android:repeatCount = "0"
    android:interpolator = "@android:anim/accelerate_interpolator">
</animator>

然後在 Activity 中獲取 ValueAnimator,設置目標對象,啟動動畫即可,具體如下:

private void translation(){
    ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this,R.animator.test_animator);
    animator.setTarget(ivImage);
    animator.start();
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Log.i(TAG, animation.getAnimatedValue() + "");
            int x = (int) animation.getAnimatedValue();
            ivImage.setTranslationX(x);
            ivImage.setTranslationY(x);
        }
    });
}

測試效果#

這裡使用 ValueAnimator 來實現平移動畫,測試效果如下:

image

ObjectAnimator#

ObjectAnimator 是 ValueAnimator 的子類,可在目標對象上支持動畫屬性的設置,在其構造方法中通過參數指定目標對象以及所對應動畫屬性的名稱,然後會相應的執行對應的動畫屬性的 setter 方法來最終完成動畫的執行,也就是說屬性動畫 ObjectAnimator 最終調用目標對象的 setter 方法完成目標對象屬性值的變化,然後相應的改變目標對象的屬性,從而實現目標對象的動畫效果,下面以透明度變化來介紹 ObjectAnimator 的基本使用,代碼參考如下:

private void alpha(){
    ObjectAnimator animator = ObjectAnimator.ofFloat(ivImage,"alpha",1f,0,1f);
    animator.setDuration(3000);
    //其他屬性動畫設置
    //...
    animator.start();
}

下面是測試效果,如下圖所示:

image

至於平移、旋轉、縮放動畫實現方式基本如上,這裡不再贅述,其對應的 setter 方法對應關係如下:

屬性作用對應方法
Alpha控制 View 的透明度setAlpha
TranslationX控制 X 方向的位移setTranslationX
TranslationY控制 Y 方向的位移setTranslationY
ScaleX控制 X 方向的縮放倍數setScaleX
ScaleY控制 Y 方向的縮放倍數setScaleY
Rotation控制以螢幕方向為軸的旋轉度數setRotation
RotationX控制以 X 軸為軸的旋轉度數setRotationX
RotationY控制以 Y 軸為軸的旋轉度數setRotationY

ObjectAnimator 提供了很多的 ofXxx () 方法來方便設置屬性動畫,如下圖所示:

image

可根據不同的動畫需求使用 ObjectValueAnimator 不同 ofXxx () 方法來實現相應的動畫。

關鍵幀#

這裡簡單說一下關鍵幀的使用,顧名思義關鍵幀就是在某個固定時刻上定義具體的屬性值,為定義的將按照估值器返回的值返回屬性值,屬性動畫中的關鍵幀使用方式如下:

/**
 * 關鍵幀的使用
 */
private void keyFrame(){
    Keyframe keyframe1 = Keyframe.ofFloat(0,0);
    Keyframe keyframe2 = Keyframe.ofFloat(0.25f,300);
    //每個KeyFrame可設置自己的插值器
    keyframe2.setInterpolator(new AccelerateInterpolator());
    Keyframe keyframe3 = Keyframe.ofFloat(0.75f,100);
    Keyframe keyframe4 = Keyframe.ofFloat(1,400);
    PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX",keyframe1,keyframe2,keyframe3,keyframe4);
    ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(ivImage,holder);
    animator.setDuration(3000);
    animator.start();
}

看一下添加關鍵幀之後對普通平移動畫的改變,實現測試效果如下:

image

插值器和估值器#

  • 插值器 (TimeInterpolator) 表示的是整個動畫期間動畫的變化規律,如加速、減速等。

Android 內置許多插值器,這些插值器基本涵蓋了實際開發中的大部分情況,具體如下:

image

如果內置的插值器不滿足需求,也可以自定義插值器。

  • 估值器 (TypeEvaluator) 表示的是在整個動畫期間各時刻屬性值的具體變化。

這裡自定義一個估值器來實現一個 View 沿正弦曲線運動,自定義估值器如下:

/**
 * 自定義估值器
 * Point封裝了坐標x和y
 */
public class SineTypeValue implements TypeEvaluator<Point> {
    @Override
    public Point evaluate(float fraction, Point startValue, Point endValue) {
        //y = sinA
        float distance = fraction * (endValue.getX() - startValue.getX());
        float x = startValue.getX() + distance;
        float y = startValue.getY() + (float) Math.sin(distance / 100 * Math.PI) * 100;
        Point point = new Point();
        point.setX(x);
        point.setY(y);
        return point;
    }
}

然後給動畫設置該估值器,監聽動畫屬性設置 View 的位置即可實現一個 View 沿正弦曲線運動,使用方式如下:

/**
 * 自定義估值器的使用
 * 正弦運動的估值器
 */
private void sina(){
    Point startPoint = new Point(ivImage.getX(),ivImage.getY());
    Point endPoint = new Point(ivImage.getX()+500,ivImage.getY());
    ValueAnimator valueAnimator = ValueAnimator.ofObject(new SineTypeValue(), startPoint, endPoint);
    valueAnimator.setDuration(5000);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Log.i(TAG, animation.getAnimatedValue() + "");
            Point point = (Point) animation.getAnimatedValue();
            ivImage.setX(point.getX());
            ivImage.setY(point.getY());
        }
    });
    valueAnimator.start();
}

測試效果如下:

image

Android 屬性動畫的總結到此結束。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。