Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android資訊 >> Android 5.0+ 高級動畫開發系列 矢量圖動畫

Android 5.0+ 高級動畫開發系列 矢量圖動畫

編輯:Android資訊

春節假期剛到,就趕緊抽出點時間寫點技術文章,這篇文章已經醞釀了很長時間了。我們經常可以看見很多擁有酷炫動畫的App,並且給人感覺具有高逼格的氣息,自從Google更新Android 5.0以來,Android世界變的異常豐富多彩,本篇主要講解 Android5.0/6.0 以後實現酷炫動畫的新技術。有矢量圖動畫VectorDrawable,靜態VectorDrawable,動態VectorDrawable,軌跡動畫,路徑變換動畫,並指出了目前常見的一些兼容性問題。干貨滿滿。

學習矢量圖動畫之前,我們需要先回顧一下屬性動畫的知識點,動態矢量圖動畫是需要結合屬性動畫來實現的。

1.屬性動畫框架

使用動態的VectorDrawable需要結合 屬性動畫 來實現,所以下面先回顧一下屬性動畫。
Android 3.0 中加入的新的屬性動畫系統。這個新的動畫系統使得任何對象做任何類型的屬性的動畫都變得容易,包括那些在 Android 3.0 中加入 View 中的新屬性。在 Android 3.1 中,又加入了一些工具類使得 View 對象做屬性動畫更加容易。

傳統動畫(Animation)是系統不斷調用onDraw方法重繪界面以實現動畫效果,不適合做交互動畫效果,只適用於做顯示動畫;而屬性動畫(Animator)則是操作一個屬性的get、set方法去真實的改變一個屬性。這裡只針對屬性動畫(Animator)進行說明。

注意:屬性動畫在Google提供的APIDemo中已經有非常多的案例實現,建議多加揣摩學習。

1.ObjectAnimator的使用

屬性動畫中最簡單最常用的就是ObjectAnimator:

1)作用單個屬性的動畫實現:

/**
  * 參數1: 操縱的控件
  * 參數2: 操縱的屬性, 常見的有偏移translationX、translationY, 絕對值x、y, 3D旋轉rotation、
  *     水平\豎直方向旋轉rotationX\rotationY, 水平\豎直方向縮放scaleX\scaleX,透明度alpha
  * 參數3,4: 變化范圍
  * setDuration: 設置顯示時長
  */
ObjectAnimator.ofFloat(imageView, "translationX", 0F, 200F).setDuration(1000).start();

同樣屬性動畫也可以在res/animator文件夾下進行創建anim.xml並配置(多個objectAnimator可以用set進行包裹):

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:androd="http://schemas.android.com/apk/res/android"
    androd:duration="1000"
    androd:propertyName="translationX"
    androd:valueFrom="0F"
    androd:valueTo="200F"
    androd:valueType="floatType">
</objectAnimator>

這段XML實現的效果和我們剛才通過代碼來實現的組合動畫的效果是一模一樣的,每個參數的含義都非常清楚,相信大家都是一看就懂,我就不再一一解釋了。

XML文件是編寫好了,那麼我們如何在代碼中把文件加載進來並將動畫啟動呢?只需調用如下代碼即可:

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

調用AnimatorInflater的loadAnimator來將XML動畫文件加載進來,然後再調用setTarget()方法將這個動畫設置到某一個對象上面,最後再調用start()方法啟動動畫就可以了,就是這麼簡單。

2)同時作用多個屬性的動畫實現:

所有動畫同時播放:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView, "translationY", 0F, 200F);
AnimatorSet set = new AnimatorSet();
set.playTogether(animator1, animator2);
set.setDuration(1000).start();

所有動畫按順序播放:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView, "translationY", 0F, 200F);
AnimatorSet set = new AnimatorSet();
set.playSequentially(animator1, animator2);
set.setDuration(1000).start();

有的同時播放有的按順序:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView, "translationY", 0F, 200F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(imageView, "rotation", 0F, 360F);
AnimatorSet set = new AnimatorSet();
set.play(animator1).with(animator2);//同時
set.play(animator3).after(animator1);//之後, 另外還有before之前
set.setDuration(1000).start();

3)屬性動畫的結束監聽事件

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

2.ValueAnimator的使用

ValueAnimator不會作用於任何一個屬性,簡單來說,它就是“數值發生器”,實際上在屬性動畫中,產生每一步的具體動畫實現效果都是通過ValueAnimator計算出來的。ObjectAnimator是繼承自ValueAnimator的,ValueAnimator並沒有ObjectAnimator使用的廣泛。
ValueAnimator通過動畫已經繼續的時間和總時間的比值產生一個0~1點時間因子,有了這樣的時間因子,經過相應的變換,就可以根據startValue和endValue來生成中間相應的值。

1)顯示ValueAnimator生成出來的數值(這裡沒有指定插值器,默認線性增長):

ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.setDuration(5000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        Integer value = (Integer) valueAnimator.getAnimatedValue();
        textView.setText(""+value);
    }
});
animator.start();

2)自定義數值生成器

ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
    /**
     * 通過重寫evaluate方法返回各種各樣的值
     * @param fraction 時間因子 0到1之間變化到數值
     * @param startValue
     * @param endValue
     * @return
     */
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        return null;
    }
});

3.屬性動畫常用方法\類總結

ValueAnimator\ObjectAnimator
AnimatorUpdateListener\AnimatorListenerAdapter   做監聽器的
PropertyValuesHolder\AnimatorSet    控制動態集合的顯示效果、順序、流程的
TypeEvaluators          值計算器
Interpolators           插值器

Interpolator圖示:

這些常用 方法\類官方API 都有詳細說明,建議多閱讀 官方API文檔。https://developer.android.google.cn/reference/android/animation/package-summary.html

下面正式開始 矢量圖動畫 的開發:

2.矢量圖動畫(VectorDrawable)

- Vector(矢量圖) 對比 Bitmap(位圖) 繪制效率 Vector依賴於CUP計算,適合圖像簡單的情況。Bitmap可借助於GPU加速,適合圖像復雜的情況。 適用情況 Vector適用於ICON、Button、ImageView的小圖片,或者需要動畫效果時。Bitmap由於在GPU中有緩存功能,所以Bitmap不能做頻繁的重繪。 加載速度 SVG快於PNG,但PNG有硬件加速,平均下來加載速度的提升彌補了繪制的速度缺陷。

VectorDrawable,矢量圖動畫。使用需要添加兼容庫,在app的build.gradle文件相關節點下添加:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}
dependencies {
    compile 'com.android.support:appcompat-v7:25.1.0'//23.2及以上
}

3.SVG和Vector

SVG是一套語法規范,在前端中使用。Vector只實現了SVG語法的Path標簽(為了提高解析效率),在Android中使用。

Vector的語法通過字母和數字的組合來描述一個路徑,不同字母則代表不同含義,例如:

M = moveto(M X,Y): 將畫筆移動到指定的坐標位置
L = lineto(L X,Y): 畫直線到指定的坐標位置
Z = closepath(): 關閉路徑

Vector還提供了一些封裝好的方法:

H = horizontal lineto(H X): 畫水平線到指定的X坐標位置
V = vertical lineto(V Y): 畫垂直線到指定的Y坐標位置

例如下面這個 Vector Asset 代表一個黑色的正方形:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:name="square"
        android:fillColor="#FF000000"
        android:pathData="M10,10 L20,10 L20,20 L10,20 z"/>
</vector>

解釋:

1)如上是依次以 M10,10 -> L20,10 -> L20,20 -> L10,20 -> z 進行繪制;
2)width/height 代表vector的大小;viewportWidth/viewportHeight 則代表把vector均勻分為24整份,pathData就按照這裡的標准來繪制。

4.使用靜態的VectorDrawable

在像ImageView、ImageButton這樣的控件中使用是非常簡單的:

<!-- 注意:這裡用的是srcCompat -->
app:srcCompat="@drawable/vector_image"

如果要在Button這種帶點擊效果的控件中使用,則需要通過selector來進行設置,並在對應Activity中開啟下面的設置:

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

5.使用動態的VectorDrawable

這裡需要 結合 屬性動畫 來實現動態的VectorDrawable效果:

1)創建VectorDrawable文件 arrow.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportHeight="24.0"
    android:viewportWidth="24.0">
    <group android:name="left">
        <path
            android:fillColor="#FF000000"
            android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
    </group>
    <group android:name="right">
        <path
            android:fillColor="#FF000000"
            android:pathData="M18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
    </group>
</vector>

Android Studio 的 Preview窗口顯示效果如下:

arrow

2)為VectorDrawable創建屬性動畫:

左邊小球的屬性動畫 anim_left.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
    xmlns:androd="http://schemas.android.com/apk/res/android"
    androd:duration="1000"
    androd:interpolator="@android:interpolator/overshoot"
    androd:propertyName="translateX"
    androd:repeatCount="infinite"
    androd:repeatMode="reverse"
    androd:valueFrom="0"
    androd:valueTo="10"
    androd:valueType="floatType">
</objectAnimator>
<!--
    duration="1000"          持續時間/毫秒
    interpolator             修飾動畫效果,定義動畫的變化率(加速,減速,重復,彈跳)
    propertyName="translateX"屬性名(還有前面回顧屬性動畫提到的屬性,另外還有顏色漸變fillColor/軌跡繪制trimPathStart)
    repeatCount="infinite"   無限次
    repeatMode="reverse"     重復模式:循環使用
    valueFrom="0"            起始值
    valueTo="10"             結束值
    valueType="floatType"    變化值的類型:浮點型變化
 -->

右邊小球的屬性動畫 anim_right.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
    xmlns:androd="http://schemas.android.com/apk/res/android"
    androd:duration="1000"
    androd:interpolator="@android:interpolator/overshoot"
    androd:propertyName="translateX"
    androd:repeatCount="infinite"
    androd:repeatMode="reverse"
    androd:valueFrom="0"
    androd:valueTo="-10"
    androd:valueType="floatType">
</objectAnimator>

3)下來我們需要配置動畫粘合劑 animated-vector(arrow_anim.xml),讓屬性動畫作用於VectorDrawable:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/arrow">
    <target
        android:animation="@animator/anim_left"
        android:name="left"/>
    <target
        android:animation="@animator/anim_right"
        android:name="right"/>
</animated-vector>

4)粘合到一起之後,我們就可以在Activity的Layout文件中引用了:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="45dp"
        app:srcCompat="@drawable/arrow_anim"
        android:onClick="anim"/>
</RelativeLayout>

5)在Activity中添加點擊事件anim:

public void anim(View view) {
    ImageView imageView = (ImageView) view;
    Drawable drawable = imageView.getDrawable();
    if (drawable instanceof Animatable) {
        ((Animatable) drawable).start();
    }
}

到此,動態的VectorDrawable就編寫完畢了,我們運行程序,點擊ImageView,出現了如下的效果:

VectorDrawable

有木有很贊。

6.VectorDrawable實現軌跡動畫

軌跡動畫關鍵的配置就是 objectAnimator 中 androd:propertyName=”trimPathStart” 屬性。

1)創建VectorDrawable文件 path.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="500"
    android:viewportWidth="500">
    <group
        android:scaleX="5.0"
        android:scaleY="5.0">
        <path
            android:name="start"
            android:pathData="M 50.0,90.0 L 82.9193546357,27.2774101308 L 12.5993502926,35.8158045183 L 59.5726265715,88.837672697 L 76.5249063296,20.0595700732 L 10.2916450361,45.1785327898 L 68.5889268818,85.4182410261 L 68.5889268818,14.5817589739 L 10.2916450361,54.8214672102 L 76.5249063296,79.9404299268 L 59.5726265715,11.162327303 L 12.5993502926,64.1841954817 L 82.9193546357,72.7225898692 L 50.0,10.0 L 17.0806453643,72.7225898692 L 87.4006497074,64.1841954817 L 40.4273734285,11.162327303 L 23.4750936704,79.9404299268 L 89.7083549639,54.8214672102 L 31.4110731182,14.5817589739 L 31.4110731182,85.4182410261 L 89.7083549639,45.1785327898 L 23.4750936704,20.0595700732 L 40.4273734285,88.837672697 L 87.4006497074,35.8158045183 L 17.0806453643,27.2774101308 L 50.0,90.0Z"
            android:strokeColor="#000000"
            android:strokeWidth="2"/>
    </group>
</vector>

Android Studio 的 Preview窗口顯示效果如下:

2)為VectorDrawable創建屬性動畫:

屬性動畫 anim_path.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:androd="http://schemas.android.com/apk/res/android">
    <objectAnimator
        androd:duration="10000"
        androd:propertyName="trimPathStart"
        androd:repeatCount="infinite"
        androd:repeatMode="reverse"
        androd:valueFrom="1"
        androd:valueTo="0"
        androd:valueType="floatType">
    </objectAnimator>
    <objectAnimator
        androd:duration="10000"
        androd:propertyName="strokeColor"
        androd:repeatCount="infinite"
        androd:repeatMode="reverse"
        androd:valueFrom="@android:color/holo_red_dark"
        androd:valueTo="@android:color/holo_blue_dark"
        androd:valueType="colorType">
    </objectAnimator>
</set>

3)下來我們需要配置動畫粘合劑 animated-vector(path_anim.xml),讓屬性動畫作用於VectorDrawable:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/path">
    <target
        android:animation="@animator/anim_path"
        android:name="start"/>
</animated-vector>

4)粘合到一起之後,我們就可以在Activity的Layout文件中引用了:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        app:srcCompat="@drawable/path_anim"
        android:onClick="anim"/>
</RelativeLayout>

5)在Activity中添加點擊事件anim:

public void anim(View view) {
    ImageView imageView = (ImageView) view;
    Drawable drawable = imageView.getDrawable();
    if (drawable instanceof Animatable) {
        ((Animatable) drawable).start();
    }
}

到此,動態的VectorDrawable就編寫完畢了,我們運行程序,點擊ImageView,出現了如下的效果:

7.VectorDrawable實現路徑變換動畫

軌跡動畫關鍵的配置就是 objectAnimator 中 androd:propertyName=”pathData” 和 androd:valueType=”pathType”屬性。這裡我們實現五角星向五邊形的變換動畫。

1)創建五角星VectorDrawable文件 fivestar.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="120dp"
    android:height="120dp"
    android:viewportHeight="64"
    android:viewportWidth="64">
    <group>
        <path
            android:name="star"
            android:fillColor="#22e171"
            android:pathData="M 48,54 L 31,42 15,54 21,35 6,23 25,23 32,4 40,23 58,23 42,35 z"
            android:strokeColor="#000000"
            android:strokeWidth="1"/>
    </group>
</vector>

Android Studio 的 Preview窗口顯示效果如下:

star

2)為VectorDrawable創建屬性動畫:

五角星的屬性動畫 anim_fivestar.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
    xmlns:androd="http://schemas.android.com/apk/res/android"
    androd:duration="3000"
    androd:propertyName="pathData"
    androd:valueFrom="M 48,54 L 31,42 15,54 21,35 6,23 25,23 32,4 40,23 58,23 42,35 z"
    androd:valueTo="M 48,54 L 31,54 15,54 10,35 6,23 25,10 32,4 40,10 58,23 54,35 z"
    androd:valueType="pathType">
</objectAnimator>

3)下來我們需要配置動畫粘合劑 animated-vector(fivestar_anim.xml),讓屬性動畫作用於VectorDrawable:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/fivestar">
    <target
        android:animation="@animator/anim_fivestar"
        android:name="star"/>
</animated-vector>

4)粘合到一起之後,我們就可以在Activity的Layout文件中引用了:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        app:srcCompat="@drawable/fivestar_anim"
        android:onClick="animL"/>
</RelativeLayout>

5)在Activity中添加點擊事件anim:

/**
  * 指該方法適用Android版本大於等於Android L
  * @param view
  */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void animL(View view) {
    ImageView imageView = (ImageView) view;
    AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getDrawable(R.drawable.fivestar_anim);
    imageView.setImageDrawable(drawable);
    if (drawable != null) {
        drawable.start();
    }
}

這裡的點擊事件和前面介紹過的略有不同,需要考慮到VectorDrawable實現路徑變換動畫的兼容性問題,故路徑變換動畫目前存在兼容性問題。不能在4.X版本運行,這一點格外注意。不過我們同樣希望Google可以在後續版本中優化路徑變換動畫,提高兼容性。

到此,動態的VectorDrawable就編寫完畢了,我們運行程序,點擊ImageView,出現了如下的效果:

8.動態VectorDrawable的兼容性問題

1.向下兼容問題

1)路徑變換動畫(Path Morphing)
在Android pre-L版本下是無法使用的,例如將圓形變換成三角形的動畫。

2)路徑插值器(Path Interpolation)
在Android pre-L版本只能使用系統的插值器(一般情況下,系統提供的27種插值器已經足夠我們使用了),不能自定義。

2.向上兼容問題

1)路徑變換動畫(Path Morphing)
在Android L版本以上需要使用代碼配置。

3.抽取string兼容問題

不支持從String.xml中讀取

<PathData>

同時這裡也希望Google可以在後面的版本中修復這些動態VectorDrawable的兼容性問題。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved