簡介——矢量動畫2種方式與流程
矢量動畫有一些不一樣的細節,這里需要提前了解,否則容易在后續使用的時候困惑。
group動畫
使用group包裹后的動畫,為什么要使用它來包裹path?因為屬性動畫要改變繪制的圖片屬性來實現動畫,可是path標簽中並沒有translateX和translateY屬性,因此,VectorDrawable要想使用屬性,就需要用group標簽來封裝path標簽,將屬性動畫作用在group標簽中,這里使用group標簽將vector圖片分成了兩組,如果圖片比較復雜,會將其分成多組。但是group不支持直接操作path的屬性。所以這種動畫形式是直接移動整個path或者矢量圖,它不能根據矢量圖參數來變化動畫。另外它的添加和啟動動畫方式與path的name的動畫不同。
path動畫
直接使用path的name的動畫,這個動畫支持trimPathStart、trimPathEnd、pathData,fillColor,fillAlpha這幾種與path有關的屬性,這種動畫形式可以直接看懂,trimPathStart與trimPathEnd是繪制步驟動畫、pathData是改變參數動畫(變形動畫)。另外它的添加和啟動動畫方式與group包裹后的動畫不同。
基本流程:
- 創建矢量圖
- 創建animator目錄(注意!是animator目錄。不是anim目錄。)
- 在animator里添加動畫xml
- 在drawable目錄里創建 animated-vector 組合器文件,組合動畫xml與矢量圖xml
- 在View 添加這個組合器
屬性說明
set
一個set中可以有多個objectAnimator,通過ordering屬性控制執行順序。ordering:取值["sequentially"|"together"], sequentially(默認)。
- <set android:ordering="sequentially"> 順序執行objectAnimator
- <set android:ordering="together"> 同時執行objectAnimator
objectAnimator
objectAnimator中一下這些屬性
- propertyName 動畫屬性名稱
- valueType 值的類型
- valueFrom 動畫的起始值
- valueTo 動畫的結束值
- repeatCount 重復次數
- repeatMode 重復模式
- duration 動畫時長
- interpolator 動畫插值
- startOffset 動畫延遲開始時間
propertyName屬性(屬性名)
propertyName:指定動作屬性如下:
- translateX X軸移動
- translateY Y軸移動
- trimPathStart 路徑開始
- trimPathEnd 路徑結束
- scaleX X軸放大或者縮小比例
- scaleY Y軸放大或者縮小比例
- rotation 旋轉
- fillAlpha 透明度
- fillColor 顏色
Vector里面的屬性基本上都可以(需要注意target里面綁定的是group就只能使用當前group的屬性,下級group或path等不能使用,綁定的path就只能使用當前path的屬性,clip-path一樣)。
valueType屬性(屬性值類型)
valueType:PropertyName指定屬性值的類型,共有四種類型,分別是:["intType"|"floatType"|"colorType"|pathType]。
valueFrom、valueTo屬性(動畫初始和結束值)
valueFrom:PropertyName指定屬性的初始值,值的類型為ValueType(如位置、顏色、透明、PathData等)。
valueTo:PropertyName指定屬性的結束值,值的類型為ValueType。
repeatCount、repeatMode屬性(動畫重復次數和樣式)
repeatCount:定義動畫重復次數,["infinite"]為無限次。
repeatMode:定義重復的行為 ["restart"|"reverse"], restart:直接跳到起始位置重新開始;reverse:倒退到起始位置重新開始。
duration屬性(動畫時間)
duration:動畫執行一次的時間,單位ms。
interpolator屬性(動畫過程控制)
interpolator:控制動畫的運行過程。Interpolator 定義了動畫的變化速度,可以實現勻速、正加速、負加速、無規則變加速等。可以自己定義,也可以使用默認的,列如:
Accelerate:加速,共有下面四種模式
["@android:interpolator/accelerate_decelerate"|"@android:interpolator/accelerate_cubic"|
"@android:interpolator/accelerate_quad"|"@android:interpolator/accelerate_ quint "]
accelerate_decelerate:在動畫開始與結束的地方速率改變比較慢,在中間的時候加速;
accelerate_cubic:擬合立方緩和函數的曲線加速。
accelerate_quad:曲線匹配的quadtratic緩解功能加速。
accelerate_ quint:曲線匹配功能昆特緩解加速。
Decelerate:減速,共有四種模式,同Accelerate。
["@android:interpolator/accelerate_decelerate"|"@android:interpolator/decelerate_cubic"|
"@android:interpolator/decelerate_quad"|"@android:interpolator/decelerate_ quint "]。
Anticipate:開始的時候向后然后向前甩["@android:interpolator/accelerate_decelerate"] 。
AnticipateOvershoot:開始的時候向后然后向前甩一定值后返回最后的值["@android:interpolator/anticipate_overshoot"] 。
Bounce:動畫結束的時候彈起["@android:interpolator/bounce"] 。
Cycle:動畫循環播放特定的次數,速率改變沿着正弦曲線["@android:interpolator/cycle"] 。
Linear:以常量速率改變["@android:interpolator/linear"] 。
Overshoot:向前甩一定值后再回到原來位置["@android:interpolator/overshoot"]。
startOffset屬性(延時)
延遲播放的時間,單位ms,只延時第一次播放,重復播放時不延時。
實現動畫
group 包裹動畫:
例子1
效果圖:
步驟一 創建矢量圖
在drawable目錄
my_svg.xml
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="50dp" android:height="50dp" android:viewportWidth="50.0" android:viewportHeight="50.0"> <group android:name="ic_right_path"> <path android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="5" android:pathData="M20,10 L35,27 M20,40 L35,24"/> </group> </vector>
步驟二 創建動畫xml
在animator目錄
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:interpolator/decelerate_cubic" android:duration="3000" android:repeatMode="reverse" android:repeatCount="infinite" android:propertyName="translateX" android:valueFrom="0" android:valueTo="10" android:valueType="floatType"> </objectAnimator>
雖然上面已經說明過屬性了,但是我還是在啰嗦解釋一下:
動畫插補器:android:interpolator="@android:interpolator/decelerate_cubic" 這里使用的是快到最后動畫減速的屬性
動畫持續時間:android:duration="3000" 這里設置的是3秒
動畫重復模式:android:repeatMode="reverse" 這里設置的是倒退到起始位置重新開始動畫
動畫屬性:android:propertyName="translateX" 這里設置的是X軸移動
動畫開始值: android:valueFrom="0"
動畫完成值:android:valueTo="10"
參數屬性:android:valueType="floatType" 這里設置的是浮點值
步驟三 創建組合器
在drawable目錄
arrow_anim.xml
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/my_svg"> <target android:animation="@animator/ic_right_path_animator" android:name="ic_right_path"/> </animated-vector>
步驟四 在布局xml上的ImageView里添加組合器 arrow_anim.xml
<ImageView android:id="@+id/ic_anim" android:layout_width="300dp" android:layout_height="300dp" android:src="@drawable/arrow_anim" android:layout_centerInParent="true" />
步驟五 啟動動畫
mIcAnim = (ImageView)findViewById(R.id.ic_anim); mBtnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Drawable drawable = mIcAnim.getDrawable(); if (drawable instanceof Animatable) { ((Animatable) drawable).start(); } } });
直接使用path name的動畫:
trimPathStart屬性
例子1:
效果圖
步驟一 創建矢量圖
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="50dp" android:height="50dp" android:viewportWidth="50.0" android:viewportHeight="50.0"> <path android:name="ic_right_path" android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="5" android:pathData="M20,10 L35,27 L20,40"/> </vector>
步驟二 創建動畫xml
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:interpolator/decelerate_cubic" android:duration="3000" android:repeatMode="reverse" android:repeatCount="infinite" android:propertyName="trimPathStart" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"> </objectAnimator>
步驟三 創建組合器
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/my_svg"> <target android:animation="@animator/ic_right_path_animator" android:name="ic_right_path"/> </animated-vector>
步驟四 添加組合器和啟動動畫
這里可以使用2種形式,別問我為什么可以使用兩種。我也不知道為什么。。。
第一種就是上面使用group包裹的動畫的形式
第二種在代碼上添加,注意這里使用的是AnimatedVectorDrawable,這很重要。因為下面的pathData屬性必須使用它
mIcAnim = (ImageView)findViewById(R.id.ic_anim); mBtnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AnimatedVectorDrawable drawable = (AnimatedVectorDrawable)getDrawable(R.drawable.arrow_anim); mIcAnim.setImageDrawable(drawable); if (drawable != null){ drawable.start(); } } });
pathData屬性
例子1:
效果圖:
步驟一 創建矢量圖
在drawable目錄
my_svg.xml
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="50dp" android:height="50dp" android:viewportWidth="50.0" android:viewportHeight="50.0"> <path android:name="ic_right_path" android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="5" android:pathData="M20,10 L35,27 M0,25 L34,25 M20,40 L35,24"/> </vector>
步驟二 在animator目錄創建動畫xml
ic_right_path_animator.xml
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:interpolator/decelerate_cubic" android:duration="3000" android:repeatMode="reverse" android:repeatCount="infinite" android:propertyName="pathData" android:valueFrom="M20,10 L35,27 M0,25 L34,25 M20,40 L35,24" android:valueTo="M0,15 L40,15 M0,25 L40,25 M0,35 L40,35" android:valueType="pathType"> </objectAnimator>
屬性已經在上面有說明了,請對着理解就行了,這里需要注意2點!注意2點!注意2點! 重要事情說三遍!
- 第一點 android:valueType="pathType" 這里使用的參數格式是pathType
- 第二點 矢量動畫要求初始幀的路徑命令序列(valueFrom)與結束幀的路徑命令序列(valueTo)內的命令必須一一對應,只有參數值可以不同,這樣才能插值,從而矢量動畫才能執行。否則編譯后運行時就崩潰了。
步驟三 創建組合器
arrow_anim.xml
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/my_svg"> <target android:animation="@animator/ic_right_path_animator" android:name="ic_right_path"/> </animated-vector>
添加動畫(這里的動畫輸入的時候沒有聯想輸入,屬於正常請手動敲完)與path名稱
步驟四 給ImageView添加arrow_anim 在啟動動畫
mIcAnim = (ImageView)findViewById(R.id.ic_anim); mBtnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AnimatedVectorDrawable drawable =(AnimatedVectorDrawable)getDrawable(R.drawable.arrow_anim); mIcAnim.setImageDrawable(drawable); if (drawable != null){ drawable.start(); } } });
注意!注意!注意!
1.請不要把arrow_anim組合器直接添加到View xml屬性里,必須在代碼上添加才可以,如果直接添加會報錯。
2.這里使用的是AnimatedVectorDrawable類型,添加到View和啟動動畫,請不要弄錯了。
例子2
步驟一 創建矢量圖
<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:fillColor="#FF000000" android:pathData="M15.5,9.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> <path android:fillColor="#FF000000" android:pathData="M8.5,9.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> <path android:fillColor="#FF000000" android:pathData="M11.99,2 C6.47,2 2,6.48 2,12 s4.47,10 9.99,10 C17.52,22 22,17.52 22,12S17.52,2 11.99,2z M12,20 c-4.42,0 -8,-3.58 -8,-8 s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/> <path android:name="expression" android:strokeColor="#FF000000" android:strokeWidth="1.5" android:pathData="M8,17 C 10,14 14,14 16,17"/> </vector>
說明一下,這里我使用的笑臉是Android studio自帶的,一開始的想法是找一個哭臉和笑臉,然后直接找到嘴巴的屬性就行了。但是,坑大了,最后發現哭臉和笑臉的嘴巴屬性根本不一樣。沒有辦法最后我只能自己直接用三次貝賽曲線畫了一個嘴巴。使用各位大神如果想直接使用Android studio的矢量圖來做path值變化,請一定檢查清楚屬性是否一樣(只要有一個字母不同就不行!)
步驟二 創建動畫xml
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:interpolator/decelerate_cubic" android:duration="3000" android:repeatMode="reverse" android:repeatCount="infinite" android:propertyName="pathData" android:valueFrom="M8,17 C 10,14 14,14 16,17" android:valueTo="M8,15 C 10,18 14,18 16,15" android:valueType="pathType"> </objectAnimator>
步驟三 創建組合器
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_sentiment_satisfied_black_24dp"> <target android:animation="@animator/expression" android:name="expression"/> </animated-vector>
步驟四 向View添加組合器、啟動動畫
mIcAnim = (ImageView)findViewById(R.id.ic_anim); mBtnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AnimatedVectorDrawable drawable = (AnimatedVectorDrawable)getDrawable(R.drawable.arrow_anim2); mIcAnim.setImageDrawable(drawable); if (drawable != null){ drawable.start(); } } });
例子3
效果圖:
步驟一 創建矢量圖
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="100dp" android:height="100dp" android:viewportWidth="100.0" android:viewportHeight="100.0"> <!--頭--> <path android:fillColor="@color/colorPrimaryDark" android:pathData="M40,20 C 40,6.5 60,6.5 60,20 M40,20 C 40,33.5 60,33.5 60,20 "/> <!--軀干--> <path android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="16" android:pathData="M48.5,32 L45,60"/> <!--左手--> <path android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="10" android:pathData="M43,35 L25,45 25,55 "/> <!--右手--> <path android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="10" android:pathData="M50,36.3 L75,57"/> <!--左腿 M43,55 L30,70 L15,65--> <path android:name="left_leg" android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="10" android:pathData="M43,55 L30,70 L15,65"/> <!--右腿 --> <path android:name="right_leg" android:strokeColor="@color/colorPrimaryDark" android:strokeWidth="10" android:pathData="M47,55 L55,70 L60,90"/> </vector>
預覽效果:
步驟 二 創建對應的動畫xml文件
創建左腿動畫 step_left.xml
<?xml version="1.0" encoding="utf-8"?> <set android:ordering="sequentially" xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1500" android:propertyName="pathData" android:valueFrom="M43,55 L30,70 L15,65" android:valueTo="M43,55 L45,70 L35,85" android:valueType="pathType"> </objectAnimator> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1500" android:propertyName="pathData" android:valueFrom="M43,55 L45,70 L35,85" android:valueTo="M47,55 L55 70 L60 90" android:valueType="pathType"> </objectAnimator> </set>
注意!這里為了讓腿部動的不那么尷尬,所以我添加了一個中間值動畫。所以這里使用的是組合動畫,在set包裹下的objectAnimator 會使用 android:ordering="sequentially"屬性按順序運行動畫。注意,我取消了android:repeatMode="reverse"
android:repeatCount="infinite" 這兩個屬性,並且使用默認的插補器,所以沒有配置插補器。
創建右腿動畫 step_right.xml
<?xml version="1.0" encoding="utf-8"?> <set android:ordering="sequentially" xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1500" android:propertyName="pathData" android:valueFrom="M43,55 L30,70 L15,65" android:valueTo="M43,55 L45,70 L35,85" android:valueType="pathType"> </objectAnimator> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1500" android:propertyName="pathData" android:valueFrom="M43,55 L45,70 L35,85" android:valueTo="M47,55 L55 70 L60 90" android:valueType="pathType"> </objectAnimator> </set>
步驟三 創建組合器
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_step"> <target android:animation="@animator/step_left" android:name="left_leg"/> <target android:animation="@animator/step_right" android:name="right_leg"/> </animated-vector>
步驟四 啟動動畫
mIcAnim = (ImageView)findViewById(R.id.ic_anim); mBtnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AnimatedVectorDrawable drawable = (AnimatedVectorDrawable)getDrawable(R.drawable.arrow_anim3); mIcAnim.setImageDrawable(drawable); if (drawable != null){ drawable.start(); } } });