目錄
Android高級動畫(1)http://www.reibang.com/p/48554844a2db
Android高級動畫(2)http://www.reibang.com/p/89cfd9042b1e
Android高級動畫(3)http://www.reibang.com/p/d6cc8d218900
Android高級動畫(4)http://www.reibang.com/p/91f8363c3a8c
來點硬貨
前面一篇文章已經(jīng)講了Android中大部分的動畫框架膘螟,回顧一下有:Tween動畫,屬性動畫胎许,幀動畫含长,CircularReveal盐股,Activity轉(zhuǎn)場動畫胎源,5.0新轉(zhuǎn)場動畫译株,Interpolator插值器瓜喇,5.0轉(zhuǎn)場動畫分為Explode、Slide歉糜、Fade乘寒、Share四種模式。合理且充分利用這些動畫匪补,我們已經(jīng)可以做出很多優(yōu)美的效果了肃续。
但是今天這篇文章我們來講講大名鼎鼎的矢量動畫黍檩,它顛覆了前面所有的動畫。前面的動畫都是對控件做動畫始锚,而矢量動畫是對圖形做動畫刽酱,矢量動畫可以做出前面任何一個動畫框架都做不到的效果。好了瞧捌,NB就先不吹了棵里,開始我們的學習吧。
從矢量圖形說起
我們平時看到的圖片大多數(shù)都是位圖姐呐,英文名叫 bitmap殿怜,位圖對應(yīng)的格式就是 .bmp,看過bmp的人都知道曙砂,體積那叫一個大啊头谜。。鸠澈。一張小小的Logo都能2M柱告,于是jpg,png這些壓縮格式就出現(xiàn)了笑陈,優(yōu)秀的壓縮算法極大地減少了圖片體積际度。配合索引位圖、灰度圖等手段涵妥,圖片可以壓縮的非常小乖菱,世界一下子變得美好~
但是,開心沒多久蓬网,問題又來了窒所。不管你的壓縮算法有所優(yōu)秀,位圖有2個天生的缺點無法避免:
(1)圖片放大會失真
(2)圖片尺寸越大帆锋,體積越大
不管是做Android開發(fā)還是IOS開發(fā)墩新,我們都需要適配不同分辨率的手機,也就意味著同一個ImageView在不同手機上的圖片分辨率是不同的窟坐,如果我們只用一套圖片海渊,那必然存在放大失真問題。統(tǒng)一的解決方案就是為每一種分辨屏幕準備一套圖片哲鸳。這樣失真的問題解決了臣疑,但是圖片體積又大了。
似乎兩者是不可兼得的徙菠,怎么辦呢讯沈?
矢量圖登場
矢量圖不同于位圖是用像素描述圖像的,它是用數(shù)學曲線描述圖形婿奔。所以一張圖片就是對應(yīng)著一系列的數(shù)學曲線缺狠,所以圖片的顯示尺寸和圖片體積無關(guān)问慎。(這里為什么說顯示尺寸,因為矢量圖根本就沒有所謂的尺寸挤茄,就看你把它顯示成多大)如叼,它的體積就是文本文件的大小。并且矢量圖可以無限拉伸不失真穷劈。
先來看一個Android中使用矢量圖的例子:
哇笼恰,這個愛心有點漂亮~
代碼實現(xiàn):
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportHeight="32"
android:viewportWidth="32">
<path
android:fillColor="#e11c00"
android:pathData="M20.5,9.5 c-1.955,0,-3.83,1.268,-4.5,3
c-0.67,-1.732,-2.547,-3,-4.5,-3 C8.957,9.5,7,11.432,7,14
c0,3.53,3.793,6.257,9,11.5 c5.207,-5.242,9,-7.97,9,-11.5
C25,11.432,23.043,9.5,20.5,9.5z" />
</vector>
(PS:這個文件非常小,只有670字節(jié)歇终,連1KB都不到社证。而且我們只需要這一個文件,就可以適配所有分辨率评凝,無限拉伸不失真追葡。)
根節(jié)點是vector,width和height屬性是顯示大小奕短,但是實際上這個大小是可以根據(jù)控件改變的宜肉。viewportHeight和viewportWidth也是寬高,它是定義曲線函數(shù)時所參照的寬高篡诽。子節(jié)點path就是定義繪制內(nèi)容的崖飘,fillColor是填充顏色榴捡,pathData是描繪路徑杈女。那么問題來了,pathData中的這一串字母數(shù)字是什么東東吊圾?
SVG
說到這达椰,SVG必須得登場了。SVG就是標準的矢量圖格式项乒,Android中使用矢量圖雖然沒有直接使用SVG圖片啰劲,但是基本格式是和SVG一樣的。
SVG語法
SVG的語法太復雜了檀何,這里不可能全部講一遍蝇裤。為了說明問題,我們就講幾個最基礎(chǔ)的命令频鉴。
M:新建起點栓辜,參數(shù)x,y(M20垛孔, 30)
L:連接直線藕甩,參數(shù)x,y(L30周荐, 20)
H:縱坐標不變狭莱,橫向連線僵娃,參數(shù)x(H20)
V:橫坐標不變,縱向連線腋妙,參數(shù)y(V30)
Q:二次貝塞爾曲線默怨,參數(shù)x1,y1辉阶,x2先壕,y2(Q10,20谆甜,30垃僚,40)
C:三次貝塞爾曲線,參數(shù)x1规辱,y1谆棺,x2,y2罕袋,x3改淑,y3(C10,20浴讯,30朵夏,40,50榆纽, 60)
Z:連接首尾仰猖,閉合曲線,無參數(shù)
掌握以上這些基本命令之后奈籽,我們基本上就可以畫出90%的圖形了饥侵。比如上面demo只用到了三個命令:M、C衣屏、Z躏升,我們整個系列所有demo用到的命令也就只有M、L狼忱、C膨疏、Z。
(至于什么是二次貝塞爾钻弄,什么是三次貝塞爾佃却,如果不了解的話請自行百度,不能再拓展了斧蜕,否則這篇文章要突破萬字了双霍。)
讓矢量圖形動起來
雖然我們已經(jīng)可以繪制漂亮的矢量圖形了,但是我們這個系列是Android高級動畫啊,得動起來洒闸,Android中怎樣才能讓矢量圖形動起來呢染坯?
animated-vector登場
animated-vector從名字上看就是動起來的vector,先看示例:
初始顯示的是三條橫線丘逸,然后從三條橫線的狀態(tài)變化到箭頭单鹿,同時整體旋轉(zhuǎn)360度。
代碼如下:
(1)首先是layout文件深纲,一個普通的ImageView仲锄,src指向一個drawable
<ImageView
android:id="@+id/imgBtn"
android:layout_width="200dp"
android:layout_height="200dp"
android:onClick="startAnim"
android:src="@drawable/animvectordrawable" />
(2)drawable根節(jié)點是一個animated-vector,drawable參數(shù)用于指定初始顯示的樣子湃鹊,下面兩個target子節(jié)點用于指定動畫儒喊,第一個target是指定了旋轉(zhuǎn)動畫,第二個target指定了path轉(zhuǎn)變動畫币呵。下面我們分別來看下初始的drawable和兩個target怀愧。
<!-- animvectordrawable.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vectordrawable" >
<target
android:name="rotationGroup"
android:animation="@anim/rotation" />
<target
android:name="v"
android:animation="@anim/path_morph" />
</animated-vector>
(3)初始drawable,這個根節(jié)點vector就是定義了寬高余赢,第三層path節(jié)點就是初始顯示的矢量圖形芯义,它有填充色和path路徑。
<!-- vectordrawable.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="300dp"
android:width="300dp"
android:viewportHeight="70"
android:viewportWidth="70" >
<group
android:name="rotationGroup"
android:pivotX="35"
android:pivotY="35"
android:rotation="0.0" >
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M10,10 L60,10 L60,20 L10,20 Z M10,30 L60,30
L60,40 L10,40 Z M10,50 L60,50 L60,60 L10,60 Z" />
</group>
</vector>
但是外面又包了一層group妻柒,這個是干什么用的呢扛拨?
animated-vector規(guī)定,可以有多個動畫同時進行举塔,但是一個對象上只能加載一個動畫绑警。上面的例子可以看到三條線圖形轉(zhuǎn)變成箭頭圖形,同時旋轉(zhuǎn)360度啤贩,那就要有兩個動畫待秃,一個做path變換拜秧,一個做旋轉(zhuǎn)痹屹。但是兩個動畫不能同時放在一個對象上,所以必須用group包一層枉氮,把path變換動畫放在path對象上志衍,把旋轉(zhuǎn)動畫放在group對象上,從而實現(xiàn)整體的效果聊替。
(4)target1楼肪,這就是一個簡單的屬性動畫,旋轉(zhuǎn)360度
<!-- rotation.xml -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1500"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
(5)target2惹悄,這個動畫是最神奇的地方春叫,它用于從一個path變換到另一個path,valueFrom指定變換前的path,valueTo指定變換后的path暂殖,propertyName和valueType在這個例子中是固定寫法价匠。
<!-- path_morph.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="1500"
android:propertyName="pathData"
android:valueFrom="M10,10 L60,10 L60,20 L10,20 Z M10,30 L60,30 L60,40
L10,40 Z M10,50 L60,50 L60,60 L10,60 Z"
android:valueTo="M5,35 L40,0 L47.072,7.072 L12.072,42.072 Z M10,30 L60,30 L60,40
L10,40 Z M12.072,27.928 L47.072,62.928 L40,70 L5,35 Z"
android:valueType="pathType" />
</set>
這里需要重點提下valueFrom和valueTo
valueFrom和valueTo分別指定了變換前的path和變換后的path,它要求前后兩個path必須是同形path
(PS:如果兩個path擁有相同的命令數(shù)呛每,并且對應(yīng)位置的命令符相同踩窖,那么這兩個path我們稱之為同形path。
如:
M10,15 L20,20 L25,15 C10,20,20,20,30,10 L50,50 Z
M20,30 L10,10 L15,25 C25,10,30,30,10,20 L35,35 Z
)
(6)java代碼啟動動畫
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void startAnim(View view) {
Drawable drawable = imgBtn.getDrawable();
((Animatable) drawable).start();
}
至此晨横,一個簡單的animated-vector就完成了洋腮,估計有人要吐槽了。
“我的天手形,一個動畫要寫這么多代碼啥供?”
是的,這一點木有辦法库糠,矢量動畫本身就比較復雜滤灯。但是別傷心,因為更復雜的還在后面呢曼玩。鳞骤。。
矢量選擇器
animated-vector已經(jīng)很強大了是吧黍判,但是肯定有人發(fā)現(xiàn)問題了豫尽,animated-vector只能從一個path變換到另一個path,不能反向再變回來顷帖。如果我需要在兩個path之間來回變換該怎么辦呢美旧?
animated-selector登場。
selector我們大家都很熟悉了贬墩,用于一個按鈕的點擊效果榴嗅。animated-selector類似,也是用于兩個狀態(tài)的切換陶舞,只不過animated-selector是在兩個path之間來回切換顯示嗽测。
先看演示:
是不是很酷炫!迫不及待地想知道是怎么實現(xiàn)的肿孵。
(1)首先是Layout唠粥,一個普通的 ImageView,src指向一個 drawable
<ImageView
android:id="@+id/iv_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/heart_twitter" />
(2)再看drawable停做,drawbale是一個animated-selector晤愧,子節(jié)點是兩個item和兩個transition。
<?xml version="1.0" encoding="utf-8"?>
<animated-selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/state_on"
android:drawable="@drawable/ic_twitter"
android:state_checked="true"/>
<item android:id="@+id/state_off"
android:drawable="@drawable/ic_heart" />
<transition
android:fromId="@id/state_off"
android:toId="@id/state_on"
android:drawable="@drawable/avd_heart_to_twitter" />
<transition
android:fromId="@id/state_on"
android:toId="@id/state_off"
android:drawable="@drawable/avd_twitter_to_heart" />
</animated-selector>
兩個item分別指定了兩種狀態(tài)下要顯示的樣子蛉腌,兩個transition分別指定了當狀態(tài)切換時所做的動畫官份。
具體來說:第一個item指定的是state_on時顯示的樣子只厘,第二個item指定的是state_off時顯示的樣子。第一個transition指定的是從off切換到on時所做的動畫舅巷,第二個transition指定的是從on切換到off時所做的動畫懈凹。
下面來分別看下兩個item和兩個transition
(3)兩個item
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<group
android:name="groupTwitter"
android:pivotX="12"
android:pivotY="12">
<path
android:name="twitter"
android:fillColor="#C2185B"
android:pathData="M 22.46,6.0 l 0.0,0.0 C 21.69,6.35,20.86,6.58,20.0,6.69
C 20.88,6.16,21.56,5.32,21.88,4.31 c 0.0,0.0,0.0,0.0,0.0,0.0
C 21.05,4.81,20.13,5.16,19.16,5.36 C 18.37,4.5,17.26,4.0,16.0,4.0
c 0.0,0.0,0.0,0.0,0.0,0.0 L 16.0,4.0 C 13.65,4.0,11.73,5.92,11.73,8.29
C 11.73,8.63,11.77,8.96,11.84,9.27 C 8.28,9.09,5.11,7.38,3.0,4.79
C 2.63,5.42,2.42,6.16,2.42,6.94 C 2.42,8.43,3.17,9.75,4.33,10.5
C 3.62,10.5,2.96,10.3,2.38,10.0 C 2.38,10.0,2.38,10.0,2.38,10.03
C 2.38,12.11,3.86,13.85,5.82,14.24 C 5.46,14.34,5.08,14.39,4.69,14.39
C 4.42,14.39,4.15,14.36,3.89,14.31 C 4.43,16.0,6.0,17.26,7.89,17.29
C 6.43,18.45,4.58,19.13,2.56,19.13 C 2.22,19.13,1.88,19.11,1.54,19.07
C 3.44,20.29,5.7,21.0,8.12,21.0 C 16.0,21.0,20.33,14.46,20.33,8.79
C 20.33,8.6,20.33,8.42,20.32,8.23 C 21.16,7.63,21.88,6.87,22.46,6.0 L 22.46,6.0"/>
</group>
</vector>
<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="150dp"
android:height="150dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0" >
<group
android:name="groupHeart"
android:pivotX="12"
android:pivotY="12">
<path
android:name="heart"
android:fillColor="#C2185B"
android:pathData="M 12.0,21.35 l -1.45,-1.32 C 5.4,15.36,2.0,12.28,2.0,8.5
C 2.0,5.42,4.42,3.0,7.5,3.0 c 1.74,0.0,3.41,0.81,4.5,2.09
C 13.09,3.81,14.76,3.0,16.5,3.0 C 19.58,3.0,22.0,5.42,22.0,8.5
c 0.0,3.78,-3.4,6.86,-8.55,11.54 L 12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 L 12.0,21.35"/>
</group>
</vector>
根節(jié)點是一個vector,里面是一個group包著一個path悄谐,和前面說的一樣介评,這個group不是必須的,往往是為了加載動畫而增加的爬舰。path是定義路徑们陆。
(PS:這里有人可能會有疑問,這些“愛心”情屹、“Twitter”的path是怎么生成的呢坪仇?這里先提前簡單地解釋下:對于簡單的圖形,我們可以手動計算垃你,比如上面三條橫線變成箭頭的例子椅文,就是手動計算點坐標的。對于復雜的圖形惜颇,比如Twitter和愛心皆刺,手動計算不現(xiàn)實,我們可以找一些輔助軟件來生成凌摄。)
(4)兩個transition
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:drawable="@drawable/ic_heart">
<target android:name="groupHeart">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:valueFrom="-360"
android:valueTo="0"
android:duration="1000" />
</aapt:attr>
</target>
<target android:name="heart">
<aapt:attr name="android:animation">
<objectAnimator
android:duration="1000"
android:propertyName="pathData"
android:valueFrom="M 12.0,21.35 l -1.45,-1.32 C 5.4,15.36,2.0,12.28,2.0,8.5 C 2.0,5.42,4.42,3.0,7.5,3.0
c 1.74,0.0,3.41,0.81,4.5,2.09 C 13.09,3.81,14.76,3.0,16.5,3.0 C 19.58,3.0,22.0,5.42,22.0,8.5
c 0.0,3.78,-3.4,6.86,-8.55,11.54 L 12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 L 12.0,21.35"
android:valueTo="M 22.46,6.0 l 0.0,0.0 C 21.69,6.35,20.86,6.58,20.0,6.69 C 20.88,6.16,21.56,5.32,21.88,4.31
c 0.0,0.0,0.0,0.0,0.0,0.0 C 21.05,4.81,20.13,5.16,19.16,5.36 C 18.37,4.5,17.26,4.0,16.0,4.0
c 0.0,0.0,0.0,0.0,0.0,0.0 L 16.0,4.0 C 13.65,4.0,11.73,5.92,11.73,8.29 C 11.73,8.63,11.77,8.96,11.84,9.27
C 8.28,9.09,5.11,7.38,3.0,4.79 C 2.63,5.42,2.42,6.16,2.42,6.94 C 2.42,8.43,3.17,9.75,4.33,10.5
C 3.62,10.5,2.96,10.3,2.38,10.0 C 2.38,10.0,2.38,10.0,2.38,10.03 C 2.38,12.11,3.86,13.85,5.82,14.24
C 5.46,14.34,5.08,14.39,4.69,14.39 C 4.42,14.39,4.15,14.36,3.89,14.31 C 4.43,16.0,6.0,17.26,7.89,17.29
C 6.43,18.45,4.58,19.13,2.56,19.13 C 2.22,19.13,1.88,19.11,1.54,19.07 C 3.44,20.29,5.7,21.0,8.12,21.0
C 16.0,21.0,20.33,14.46,20.33,8.79 C 20.33,8.6,20.33,8.42,20.32,8.23 C 21.16,7.63,21.88,6.87,22.46,6.0 L 22.46,6.0"
android:valueType="pathType" />
</aapt:attr>
</target>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:drawable="@drawable/ic_twitter">
<target android:name="groupTwitter">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="-360"
android:duration="1000" />
</aapt:attr>
</target>
<target android:name="twitter">
<aapt:attr name="android:animation">
<objectAnimator
android:duration="1000"
android:propertyName="pathData"
android:valueFrom="M 22.46,6.0 l 0.0,0.0 C 21.69,6.35,20.86,6.58,20.0,6.69 C 20.88,6.16,21.56,5.32,21.88,4.31
c 0.0,0.0,0.0,0.0,0.0,0.0 C 21.05,4.81,20.13,5.16,19.16,5.36 C 18.37,4.5,17.26,4.0,16.0,4.0
c 0.0,0.0,0.0,0.0,0.0,0.0 L 16.0,4.0 C 13.65,4.0,11.73,5.92,11.73,8.29 C 11.73,8.63,11.77,8.96,11.84,9.27
C 8.28,9.09,5.11,7.38,3.0,4.79 C 2.63,5.42,2.42,6.16,2.42,6.94 C 2.42,8.43,3.17,9.75,4.33,10.5
C 3.62,10.5,2.96,10.3,2.38,10.0 C 2.38,10.0,2.38,10.0,2.38,10.03 C 2.38,12.11,3.86,13.85,5.82,14.24
C 5.46,14.34,5.08,14.39,4.69,14.39 C 4.42,14.39,4.15,14.36,3.89,14.31 C 4.43,16.0,6.0,17.26,7.89,17.29
C 6.43,18.45,4.58,19.13,2.56,19.13 C 2.22,19.13,1.88,19.11,1.54,19.07 C 3.44,20.29,5.7,21.0,8.12,21.0
C 16.0,21.0,20.33,14.46,20.33,8.79 C 20.33,8.6,20.33,8.42,20.32,8.23 C 21.16,7.63,21.88,6.87,22.46,6.0 L 22.46,6.0"
android:valueTo="M 12.0,21.35 l -1.45,-1.32 C 5.4,15.36,2.0,12.28,2.0,8.5 C 2.0,5.42,4.42,3.0,7.5,3.0
c 1.74,0.0,3.41,0.81,4.5,2.09 C 13.09,3.81,14.76,3.0,16.5,3.0 C 19.58,3.0,22.0,5.42,22.0,8.5
c 0.0,3.78,-3.4,6.86,-8.55,11.54 L 12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35 C 12.0,21.35,12.0,21.35,12.0,21.35
C 12.0,21.35,12.0,21.35,12.0,21.35 L 12.0,21.35"
android:valueType="pathType"/>
</aapt:attr>
</target>
</animated-vector>
兩個transition分別定義了從 off 到 on 和從 on 到 off 時切換動畫羡蛾。
根節(jié)點是一個animated-vector,它有一個重要的屬性drawable锨亏,這個屬性指定動畫前的初始狀態(tài)痴怨,就是一個普通的vector。
下面兩個target是定義動畫器予,每一個target都有一個name屬性浪藻,指定動畫作用于哪個對象上,這個對象就是上面drawable里定義的名字乾翔。<aapt:attr>這一層就是固定寫法爱葵,我也不知道為什么要定義這一層~[/尷尬]。再下面就是ObjectAnmator末融,定義具體的動畫钧惧。通過propertyName來區(qū)分動畫類型暇韧,rotation是旋轉(zhuǎn)勾习,pathData是path轉(zhuǎn)換。旋轉(zhuǎn)動畫就不說了懈玻,path動畫轉(zhuǎn)換前面也分析過了巧婶。
OK乾颁,至此我們已經(jīng)把動畫都定義好了。因為比較復雜艺栈,我們再來捋一遍英岭。
(1)首先定義一個animated-selector,它定義兩個item湿右,對應(yīng)兩種狀態(tài)on诅妹、off的顯示,再定義兩個transition用于狀態(tài)變化時啟動動畫毅人。
(2)兩個item是vector類型吭狡,定義要顯示的path。
(3)兩個transition是animated-vector類型丈莺,定義從一個狀態(tài)到另一個狀態(tài)時的動畫划煮,在指定動畫時要注意,一個對象上只能加載一個動畫缔俄,如果動畫個數(shù)比對象個數(shù)多弛秋,要用group把對象包裹起來。
可是問題來了俐载,這樣只是定義好了動畫蟹略,但是還是動不起來啊。因為animated-selector怎么知道View的狀態(tài)變化了呢遏佣?所以還差最后一步科乎,把View的狀態(tài)和animated-selector關(guān)聯(lián)起來。
private boolean isTwitterChecked = false;
public void onTwitterClick(View view) {
isTwitterChecked = !isTwitterChecked;
final int[] stateSet = {android.R.attr.state_checked * (isTwitterChecked ? 1 : -1)};
imageView.setImageState(stateSet, true);
}
好啦贼急,這樣當我們點擊圖片時茅茂,通過調(diào)用imageView.setImageState就可以切換狀態(tài),從而切換 Twitter 和 heart 的顯示太抓。再來欣賞下動畫吧空闲。
trimPath
trimPath也是一種動畫類型,它是通過對路徑的裁剪實現(xiàn)的動畫走敌。先看示例:
效果還是比較酷炫的碴倾,代碼實現(xiàn)和上面Twitter基本類似。直接貼代碼:
(1)animated-selector基本和上面類似掉丽,就不分析了
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/state_on"
android:drawable="@drawable/bar"
android:state_checked="true"/>
<item android:id="@+id/state_off"
android:drawable="@drawable/search" />
<transition
android:fromId="@id/state_off"
android:toId="@id/state_on"
android:drawable="@drawable/avd_search_to_bar" />
<transition
android:fromId="@id/state_on"
android:toId="@id/state_off"
android:drawable="@drawable/avd_bar_to_search" />
</animated-selector>
(2)兩個item也基本類似跌榔,也不分析了
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="48dp"
android:viewportWidth="150"
android:viewportHeight="24">
<path
android:name="search"
android:pathData="M141,17 A9,9 0 1,1 142,16 L149,22"
android:strokeWidth="2"
android:strokeColor="#f51035"
android:strokeAlpha="0.8"
android:strokeLineCap="round"
android:trimPathStart="1"/>
<path
android:name="bar"
android:pathData="M10,22 L149,22"
android:strokeWidth="2"
android:strokeColor="#f51035"
android:strokeAlpha="0.8"
android:strokeLineCap="round" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="48dp"
android:viewportWidth="150"
android:viewportHeight="24">
<path
android:name="search"
android:pathData="M141,17 A9,9 0 1,1 142,16 L149,22"
android:strokeWidth="2"
android:strokeColor="#f51035"
android:strokeAlpha="0.8"
android:strokeLineCap="round" />
<path
android:name="bar"
android:pathData="M10,22 L149,22"
android:strokeWidth="2"
android:strokeColor="#f51035"
android:strokeAlpha="0.8"
android:strokeLineCap="round"
android:trimPathStart="1"/>
</vector>
(3)兩個transition,這里和前面稍有不同捶障。我們拿最后一個分析下僧须。
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:drawable="@drawable/bar">
<target android:name="search">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="trimPathStart"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:duration="1000" />
</aapt:attr>
</target>
<target android:name="bar">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="trimPathStart"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:duration="1000" />
</aapt:attr>
</target>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:drawable="@drawable/search">
<target android:name="search">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="trimPathStart"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:duration="1000" />
</aapt:attr>
</target>
<target android:name="bar">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="trimPathStart"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:duration="1000" />
</aapt:attr>
</target>
</animated-vector>
首先根節(jié)點是一個animated-vector,有一個drawable屬性项炼,下面包含兩個target子節(jié)點担平,整體結(jié)構(gòu)上沒有變化示绊。區(qū)別就在target中的ObjectAnimator。
propertyName是trimPathStart暂论,表示這是一個trimPath類型的動畫(還有trimPathEnd面褐,方向相反)。trimPath的原理是從一段path上裁剪出一小部分顯示取胎,通過改變裁剪的長度展哭,形成一個漸變的動畫。
上面的demo中闻蛀,其實是有兩段path摄杂,一段是放大鏡,一段是橫線循榆。就像這樣:
初始狀態(tài)析恢,橫線顯示的長度是0,所以我們只能看到一個放大鏡:
動畫開始后秧饮,放大鏡裁剪的部分逐漸變小映挂,橫線裁剪的部分逐漸變大,直至放大鏡消失盗尸,只剩橫線柑船。
trimPath動畫的寫法也基本是固定的
<objectAnimator
android:propertyName="trimPathStart"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:duration="1000" />
valueFrom和valueTo表示裁剪的起始點和結(jié)束點,valueType是float類型泼各,duration是1000毫秒鞍时。這樣就實現(xiàn)了放大鏡和橫線切換顯示的動畫啦。
總結(jié)
這一篇我們基本講完了Android中的矢量動畫扣蜻,這塊知識點都不難逆巍,就是太亂。我盡量把思路捋的順一點了莽使,用問題引出問題的方式把所有知識點串起來锐极,這樣更容易理清關(guān)系。如果完整看到這里的話你一定會發(fā)現(xiàn)還是有問題,Android系統(tǒng)提供的vector、animated-vector悠咱、animated-selector雖然很強大,但是有一個致命的缺點赌朋,就是只能在xml中寫死,不能通過java代碼動態(tài)構(gòu)建,并且我們不能控制動畫的過程。所以這又是個頭疼的問題汪榔。怎么辦呢,下一個靚仔在哪里罕拂?
下一篇
下一篇應(yīng)該是這個系列總結(jié)篇揍异,我們會在系統(tǒng)矢量動畫的基礎(chǔ)上封裝一些自己的庫全陨,實現(xiàn)一些額外的功能爆班。最后我們還會封裝一個通用動畫庫衷掷,簡化動畫的使用。