導(dǎo)語
黑科技來了,Google在Android5.X中增加了對SVG矢量圖形的支持,這對于創(chuàng)造新的高效率動畫具有很深遠的意義。
主要內(nèi)容
- < path >標(biāo)簽
- SVG常見指令
- SVG編輯器
- Android中使用SVG
- SVG動畫實例
具體內(nèi)容
首先,我們來了解一下什么是SVG:
- 可伸縮矢量圖形
- 定義用于網(wǎng)絡(luò)的基于矢量的圖形
- 使用xml格式定義圖形
- 圖片在放大或者改變尺寸的情況下其圖形質(zhì)量不會有所損失
- 萬維網(wǎng)聯(lián)盟的標(biāo)準
- 與諸多DOM和XSL之類的W3C標(biāo)準是一個整體
SVG在web上應(yīng)用非常廣泛,在Android5.X之前的Android版本上,大家可以通過一些第三方庫在Android中使用SVG,而在Android5.X后,Android中添加了對< path >標(biāo)簽的支持,從而讓開發(fā)者可以使用SVG來創(chuàng)建更加豐富的動畫效果,那么SVG對傳統(tǒng)的Bitmap,究竟有什么好處呢?bitmap通過每個像素點上存儲色彩信息來表達圖像,而SVG是一個繪圖標(biāo)準,與之相對,最大的優(yōu)勢是SVG放大不會失真,而且bitmap需要不同分辨率適配,SVG不需要。
< path >標(biāo)簽
使用< path >標(biāo)簽來創(chuàng)建SVG,就是用指令的方式來控制一支畫筆,列入,移動畫筆來到某一個坐標(biāo)位置,畫一條線,畫一條曲線,結(jié)束,< path >標(biāo)簽所支持的指令大致有一下幾種:
- M = moveto(M X,Y):將畫筆移動到指定的坐標(biāo)位置,但未發(fā)生繪制。
- L = lineto(L X,Y):畫直線到指定的位置。
- H = horizontal lineto( H X):畫水平線到指定的X坐標(biāo)位置。
- V = vertical lineto(V Y ):畫垂直線到指定的Y坐標(biāo)。
- C = curveto(C ,X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞爾曲線。
- S = smooth curveto(S X2,Y2,ENDX,ENDY):三次貝塞爾曲線。
- Q = quadratic Belzier curve(Q X Y,ENDX,ENDY):二次貝塞爾曲線。
- T = smooth quadratic Belzier curvrto(T,ENDX,ENDY):映射前面路徑的重點。
- A = elliptical Are(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線。
- Z = closepath():關(guān)閉路徑。
使用上面的指令時,需要注意的幾點:
- 坐標(biāo)軸以(0,0)位中心,X軸水平向右,Y軸水平向下。
- 所有指令大小寫均可,大寫絕對定位,參照全局坐標(biāo)系,小寫相對定位,參照父容器坐標(biāo)系。
- 指令和數(shù)據(jù)間的空格可以無視。
- 同一指令出現(xiàn)多次可以用一個。
SVG常見指令
- L
繪制直線的指令是“L”,代表從當(dāng)前點繪制直線到給定點,“L”之后的參數(shù)是一個點坐標(biāo),如“L 200 400”繪制直線,同時,還可以使用“H”和“V”指令來繪制水平豎直線,后面的參數(shù)是x坐標(biāo)個y坐標(biāo)。
- M
M指令類似Android繪圖中的path類moveto方法,即代表畫筆移動到某一點,但并不發(fā)生繪圖動作。
- A
A指令是用來繪制一條弧線,且允許弧線不閉合,可以把A指令繪制的弧度想象成橢圓的某一段A指令一下有七個指令。
- RX,RY指所有的橢圓的半軸大小。
- XROTATION 指橢圓的X軸和水平方向順時針方向的夾角,可以想象成。一個水平的橢圓饒中心點順時針旋轉(zhuǎn)XROTATION 的角度。
- FLAG1 只有兩個值,1表示大角度弧度,0為小角度弧度。
- FLAG2 只有兩個值,確定從起點到終點的方向1順時針,0逆時針。
- X,Y為終點坐標(biāo)。
SVG的指令參數(shù)非常的復(fù)雜,但是再Android中,不需要繪制太多的SVG圖像,后面會有幾個小案例。
SVG編輯器
SVG參數(shù)的寫法固定而且復(fù)雜,因此完全可以使用程序來實現(xiàn),所以一般通過SVG編輯器來編輯SVG圖像,網(wǎng)上有很多在線的編輯器,通過可視化編輯圖像之后,點擊view→source可以轉(zhuǎn)換成SVG代碼。
下載離線的SVG編輯器,由很多強大的功能,這里就不一一贅述了。
SVG轉(zhuǎn)換器
Android中使用SVG
Google在Android5.X后給我們提供了兩個新的API來支持SVG:
- VectorDrawable
- AnimatedVectorDrawable
其中,VectorDrawable可以讓你創(chuàng)建基于XML的SVG圖像,并且結(jié)合AnimatedVectorDrawable來實現(xiàn)動畫效果。
VectorDrawable
在XML中創(chuàng)建一個靜態(tài)的SVG,通常是如下結(jié)構(gòu)。
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
</vector>
這個代碼之中包含了兩組高寬,width和height是表示SVG圖像的具體大小,后面的是表示SVG圖像劃分的比例,后面再繪制path時所使用的參數(shù),就是根據(jù)這兩個值來進行轉(zhuǎn)換的,比如上面的代碼,將200dp劃分100份,如果在繪圖中使用坐標(biāo)(50,50),則意味著該坐標(biāo)為正中間,現(xiàn)在我們加上path標(biāo)簽。
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group
android:name="svg1"
android:rotation="0">
<path
android:fillColor="@android:color/holo_blue_light"
android:pathData="M 25 50 a 25,25 0 1,0 50,0" />
</group>
</vector>
通過添加< path>標(biāo)簽繪制一個SVG圖形,pathData就是圖形所用到的指令了,先用M指令,將畫筆移動到(25 , 50)的位置,再通過A指令來繪制一個圓弧并且填充他,通過以上代碼,就可以繪制一個SVG圖形了。
上面使用android:fillColor屬性來繪制填充圖形,如果要繪制非填充的圖形可以使用以下屬性。
android:strokeColor="@android:color/holo_blue_light"
android:strokeWidth="2"
AnimatedVectorDrawable
AnimatedVectorDrawable的作用是給VectorDrawable提供動畫效果,Google的工程師將AnimatedVectorDrawable比喻一個膠水,通過AnimatedVectorDrawable來連接靜態(tài)的VectorDrawable和動態(tài)的objectAnimator。
下面我們來看看具體是怎么樣來做的,首先我們在xml中定義一個< animated-vector>,來申明對AnimatedVectorDrawable的使用,并且指明是作用在path或者group上。
- anim_vector.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/verctor">
<target
android:name="test"
android:animation="@animator/anim_rotate" >
</target>
</animated-vector>
對應(yīng)的vector即為靜態(tài)的VectorDrawable。
- vector.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="200dp"
android:width="200dp"
android:viewportWidth="100"
android:viewportHeight="100">
<group
android:name="test"
android:rotation="0"
>
<path
android:strokeColor="@android:color/holo_blue_light"
android:strokeWidth="2"
android:pathData="
M 25 50
a 25 , 25 0 1 , 0 50 ,0"
>
</path>
</group>
</vector>
需要注意的是,AnimatedVectorDrawable中指明的target和name屬性,必須與VectorDrawable中需要的name保持一致,這樣系統(tǒng)能找到找到要實現(xiàn)的動畫元素,最后,通過AnimatedVectorDrawable中的target和animation屬性,將一個動畫作用在對應(yīng)的name上,objectAnimator代碼如下。
- anim_rotat.xml:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360">
</objectAnimator>
最后,我們看到的,對動畫效果的實現(xiàn),還是通過屬性動畫來完成的,只是屬性稍有不同。
在< group>標(biāo)簽和< path>標(biāo)簽中添加rotation,fillColor,pathData屬性,那么在objecyAnimator中,就可以通過指定,android:valueFrom=XXX,和android:property=”XXX”屬性,控制動畫的起始值,唯一需要注意的是,如果指定屬性為pathData,那么需要添加一個屬性android:valueType-“pathType”來告訴系統(tǒng)進行pathData變換,類似的情況,可以使用rotation來進行旋轉(zhuǎn)變換,使用fileColor實現(xiàn)變換顏色,使用pathData進行形狀,位置的變換。
當(dāng)所有的XML準備好之后,我們就可以直接給一個imageview設(shè)置背景了。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/status_view"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/anim_vector"
/>
</LinearLayout>
到這里就有些不同了,書上寫的是,程序中使用如下代碼就可以開始SVG動畫。
Drawable drawable = imageView.getDrawable();
if(drawable instanceof Animatable) {
((Animatable) drawable).start();
}
上面的方法只能是一個動畫,可能不太好控制,如下代碼也可以使用SVG動畫。
public class SvgActivity extends Activity {
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_svg);
imageView = (ImageView) findViewById(R.id.image);
imageView.setResource(R.drawable.anim_vector); // 設(shè)置Resource為動畫資源
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
anim.start(); // 開始動畫
}
});
}
}
效果如下:
SVG動畫實例
線圖動畫
在android5.X之后,Google大量引入了線圖動畫,當(dāng)頁面發(fā)生改變的時候,頁面的icon不再是生硬的切換,而是通過非常生動的動畫,轉(zhuǎn)換成另一種形態(tài)。
我們要實現(xiàn)的效果就是上下兩根線,然后他們形成一個X的效果。
- 定義我們的VectorDrawable
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group>
<path
android:name="path1"
android:pathData="
M 20,80
L 50,80 80,80"
android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="5" />
<path
android:name="path2"
android:pathData="
M 20,20
L 50,20 80,20"
android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="5" />
</group>
</vector>
path1和path2分別繪制了一條直線,每條線都有三個點控制,接下來就是變換的動畫了。
- 定義兩個Path的objectAnimator,從path1到path2
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="pathData"
android:valueFrom="
M 20,80
L 50,80 80,80"
android:valueTo="
M 20,80
L 50,50 80,80"
android:valueType="pathType" />
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="pathData"
android:valueFrom="
M 20,20
L 50,20 80,20"
android:valueTo="
M 20,20
L 50,50 80,20"
android:valueType="pathType" />
這兩個值是對應(yīng)的起始點,不過需要注意的是,SVG的路徑變換屬性動畫中,變換前后階段屬必須相同,這也是前面需要使用的三個點看來繪制一條直線的原因,有了VectorDrawable和objectAnimator,現(xiàn)在只需要AnimatedVectorDrawable將他們黏合在一起即可。
- 定義AnimatedVectorDrawable連接VectorDrawable和objectAnimator
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/trick">
<target
android:name="path1"
android:animation="@anim/anim_path1" />
<target
android:name="path2"
android:animation="@anim/anim_path2" />
</animated-vector>
針對trick這樣的一個VectorDrawable中的path1和path2的路徑,分別使用了objectAnimator,最后只需要去啟動動畫就可以了。
- 在代碼中使用這個AnimatedVectorDrawable
ImageView image = (ImageView) findViewById(R.id.image);
Drawable drawable = image.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
代碼就完成了,接下來看一下效果吧。
- 效果圖
模擬三球儀
三球儀是天體文學(xué)中的星象儀器,用來模擬地球,月亮和太陽的運行軌跡,如圖。
實現(xiàn)過程如下:
- 定義我們的VectorDrawable
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group
android:name="sun"
android:pivotX="60"
android:pivotY="50"
android:rotation="0">
<path
android:name="path_sun"
android:fillColor="@android:color/holo_blue_light"
android:pathData="
M 50,50
a 10,10 0 1,0 20,0
a 10,10 0 1,0 -20,0" />
<group
android:name="earth"
android:pivotX="75"
android:pivotY="50"
android:rotation="0">
<path
android:name="path_earth"
android:fillColor="@android:color/holo_orange_dark"
android:pathData="
M 70,50
a 5,5 0 1,0 10,0
a 5,5 0 1,0 -10,0" />
<group>
<path
android:fillColor="@android:color/holo_green_dark"
android:pathData="
M 90,50
m -5 0
a 4,4 0 1,0 8 0
a 4,4 0 1,0 -8,0" />
</group>
</group>
</group>
</vector><?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group
android:name="sun"
android:pivotX="60"
android:pivotY="50"
android:rotation="0">
<path
android:name="path_sun"
android:fillColor="@android:color/holo_blue_light"
android:pathData="
M 50,50
a 10,10 0 1,0 20,0
a 10,10 0 1,0 -20,0" />
<group
android:name="earth"
android:pivotX="75"
android:pivotY="50"
android:rotation="0">
<path
android:name="path_earth"
android:fillColor="@android:color/holo_orange_dark"
android:pathData="
M 70,50
a 5,5 0 1,0 10,0
a 5,5 0 1,0 -10,0" />
<group>
<path
android:fillColor="@android:color/holo_green_dark"
android:pathData="
M 90,50
m -5 0
a 4,4 0 1,0 8 0
a 4,4 0 1,0 -8,0" />
</group>
</group>
</group>
</vector>
- 定義兩個objectAnimator,代碼都是一樣的
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
- 定義AnimatedVectorDrawable連接VectorDrawable和objectAnimator
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/earth_moon_system">
<target
android:name="sun"
android:animation="@anim/anim_sun" />
<target
android:name="earth"
android:animation="@anim/anim_earth" />
</animated-vector>
- 布局文件中使用這個AnimatedVectorDrawable
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/sun_system" />
- 代碼中啟動動畫
ImageView image = (ImageView) findViewById(R.id.image);
Drawable drawable = image.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
- 效果圖
- 解釋
可以在代碼中發(fā)現(xiàn)“sun”在這個group中,有一個“earth”的group,同時使用android:pivotX和android:pivotY屬性來設(shè)置其旋轉(zhuǎn)中心,分別代表太陽系統(tǒng)和地月系統(tǒng),分別對這兩個group進行SVG動畫。
軌跡動畫
使用軌跡動畫:
- 定義我們的VectorDrawable
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="160dp"
android:height="30dp"
android:viewportHeight="30"
android:viewportWidth="160">
<path
android:name="search"
android:pathData="M141 , 17 A9 ,9 0 1 , 1 ,142 , 16 L149 ,23"
android:strokeAlpha="0.8"
android:strokeColor="#ff3570be"
android:strokeLineCap="square"
android:strokeWidth="2" />
<path
android:name="bar"
android:pathData="M0,23 L149 ,23"
android:strokeAlpha="0.8"
android:strokeColor="#ff3570be"
android:strokeLineCap="square"
android:strokeWidth="2"
/>
</vector>
- 定義objectAnimator
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="trimPathStart"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" />
- 定義AnimatedVectorDrawable連接VectorDrawable和objectAnimator
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/searchbar">
<target
android:name="search"
android:animation="@anim/anim_searchbar" />
</animated-vector>
- 布局文件中使用這個AnimatedVectorDrawable
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/search_anim" />
- 代碼中啟動動畫
ImageView image = (ImageView) findViewById(R.id.image);
Drawable drawable = image.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
- 效果圖
- 解釋
還是利用屬性動畫,只是將propertyName改為trimPathStart,這個屬性就是利用0-1的百分比來按照繪制軌跡繪制SVG圖像。類似的還有trimPathEnd這個屬性,可以從無到有。