如何使用Android的VectorDrawable類繪制矢量圖
譯文繪制矢量圖形非難事——如何使用Android的VectorDrawable類
內(nèi)容概述
盡管Android系統(tǒng)并不能夠直接支持SVG(即可縮放矢量圖形),但Lollipop版本卻引入了一個(gè)名為VectorDrawable的新類,其允許設(shè)計(jì)人員及開發(fā)人員以純代碼方式生成類似的繪制效果。
在今天的文章中,我們將共同學(xué)習(xí)如何利用XML文件創(chuàng)建一個(gè)VectorDrawable,并將其以動(dòng)畫方式顯示在自己的項(xiàng)目當(dāng)中。這項(xiàng)功能只能在運(yùn)行有Android 5.0或者更高版本的設(shè)備上實(shí)現(xiàn),而且目前還不具備任何支持庫(kù)實(shí)現(xiàn)。本篇教程中的相關(guān)源文件可以通過(guò)GitHub網(wǎng)站獲取。
1. 創(chuàng)建Vector Drawable
從相似角度來(lái)看,VectorDrawable與標(biāo)準(zhǔn)SVG圖形都是利用path值繪制完成的。不過(guò)如何利用SVG path繪制圖形并不在本篇文章的探討范圍之內(nèi),大家可以點(diǎn)擊此處從W3C網(wǎng)站處獲取必要的說(shuō)明資料。在本文當(dāng)中,我們只需要了解到path標(biāo)簽的作用是進(jìn)行圖形繪制即可。讓我們首先從SVG文件入手,看看以下圖形是如何被繪制出來(lái)的:
這一圖形共由五個(gè)主要部分所組成:
一個(gè)圓角四邊形作為CPU主體,該四邊形由兩條拱狀弧線構(gòu)成。
四組各自包含五根線條的圖形,用于充當(dāng)CPU的外延線路。
以下代碼所示為如何以SVG方式繪制以上圖形:
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
- <path id="cpu" d="
- M341.087,157.478c7.417,0,13.435,6.018,13.435,13.435v170.174 c0,7.417-6.018,13.435-13.435,13.435H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913 c0-7.417,6.018-13.435,13.435-13.435H341.087z
- M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785 0,35.826-16.041,35.826-35.826V157.478z
- M193.304,408.261V462h-17.913 v-53.739H193.304z
- M264.957,408.261V462h-17.914v-53.739H264.957z
- M300.783,408.261V462h-17.914v-53.739H300.783z
- M229.13,408.261 V462h-17.913v-53.739H229.13z
- M336.609,408.261V462h-17.914v-53.739H336.609z
- M193.304,50v53.739h-17.913V50H193.304z
- M264.957,50 v53.739h-17.914V50H264.957z
- M300.783,50v53.739h-17.914V50H300.783z
- M229.13,50v53.739h-17.913V50H229.13z
- M336.609,50v53.739 h-17.914V50H336.609z
- M408.261,318.695H462v17.914h-53.739V318.695z
- M408.261,247.043H462v17.914h-53.739V247.043z
- M408.261,211.217 H462v17.913h-53.739V211.217z
- M408.261,282.869H462v17.914h-53.739V282.869z
- M408.261,175.391H462v17.913h-53.739V175.391z
- M50,318.695h53.739v17.914H50V318.695z
- M50,247.043h53.739v17.914H50V247.043z
- M50,211.217h53.739v17.913H50V211.217z
- M50,282.869 h53.739v17.914H50V282.869z
- M50,175.391h53.739v17.913H50V175.391z" />
- </svg>
雖然看起來(lái)有點(diǎn)繁雜,但大家其實(shí)用不著糾結(jié)于以上代碼的具體含義,而且這完全不會(huì)影響到我們接下來(lái)要進(jìn)行的VectorDrawable繪制工作。不過(guò)需要強(qiáng)調(diào)的是,我將前面提到的五大圖形組成部分在代碼中作為獨(dú)立的區(qū)塊來(lái)處理,這是為了增強(qiáng)代碼內(nèi)容的可讀性。
首先,我們需要利用兩條拱形弧線來(lái)繪制出圓角四邊形,而在接下來(lái)的內(nèi)容中我們會(huì)探討如何分別表現(xiàn)出上、下、左、右四個(gè)方位的外延線條。為了將上述SVG代碼轉(zhuǎn)化為VectorDrawable,大家首先需要在XML當(dāng)中定義vector對(duì)象。以下代碼提取自本篇文章示例代碼當(dāng)中的vector_drawable_cpu.xml文件。
在此之后,大家可以向其中添加path數(shù)據(jù)。下列代碼同樣被拆分成了五個(gè)不同的path標(biāo)簽而非將其作為整體處理,這當(dāng)然也是為了保證內(nèi)容的可讀性。
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="64dp"
- android:width="64dp"
- android:viewportHeight="600"
- android:viewportWidth="600" >
- </vector>
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="64dp"
- android:width="64dp"
- android:viewportHeight="600"
- android:viewportWidth="600" >
- <path
- android:name="cpu"
- android:fillColor="#000000"
- android:pathData="
- M341.087,157.478 c7.417,0,13.435,6.018,13.435,13.435 v170.174c0,7.417-6.018,13.435-13.435,13.435 H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913c0-7.417,6.018-13.435,13.435-13.435H341.087z
- M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785,0,35.826-16.041,35.826-35.826V157.478z"
- />
- <path
- android:name="wires_bottom"
- android:fillColor="#000000"
- android:pathData="
- M193.304,408.261V462h-17.913 v-53.739H193.304z
- M264.957,408.261V462h-17.914v-53.739H264.957z
- M300.783,408.261V462h-17.914v-53.739H300.783z
- M229.13,408.261 V462h-17.913v-53.739H229.13z
- M336.609,408.261V462h-17.914v-53.739H336.609z"
- />
- <path
- android:name="wires_top"
- android:fillColor="#000000"
- android:pathData="
- M193.304,50v53.739h-17.913V50H193.304z
- M264.957,50 v53.739h-17.914V50H264.957z
- M300.783,50v53.739h-17.914V50H300.783z
- M229.13,50v53.739h-17.913V50H229.13z
- M336.609,50v53.739 h-17.914V50H336.609z"
- />
- <path
- android:name="wires_right"
- android:fillColor="#000000"
- android:pathData="
- M408.261,318.695H462v17.914h-53.739V318.695z
- M408.261,247.043H462v17.914h-53.739V247.043z
- M408.261,211.217 H462v17.913h-53.739V211.217z
- M408.261,282.869H462v17.914h-53.739V282.869z
- M408.261,175.391H462v17.913h-53.739V175.391z"
- />
- <path
- android:name="wires_left"
- android:fillColor="#000000"
- android:pathData="
- M50,318.695h53.739v17.914H50V318.695z
- M50,247.043h53.739v17.914H50V247.043z
- M50,211.217h53.739v17.913H50V211.217z
- M50,282.869 h53.739v17.914H50V282.869z
- M50,175.391h53.739v17.913H50V175.391z"
- />
- </vector>
正如大家所見,每個(gè)path片段都只需要利用pathData屬性進(jìn)行繪制?,F(xiàn)在我們可以將VectorDrawable XML文件作為一個(gè)可繪制對(duì)象納入到標(biāo)準(zhǔn)ImageView當(dāng)中,而且其能夠根據(jù)應(yīng)用程序的實(shí)際需要任意進(jìn)行尺寸縮放——完全不需要再修改任何Java代碼。
2. 為Vector Drawables添加動(dòng)畫效果
現(xiàn)在我們已經(jīng)了解了如何以純代碼方式創(chuàng)建圖形,接下來(lái)要做的是找點(diǎn)樂(lè)子——為其添加動(dòng)畫效果。在以下動(dòng)畫中,大家會(huì)發(fā)現(xiàn)作為延伸線路的各組線條會(huì)不斷指向并遠(yuǎn)離CPU本體進(jìn)行移動(dòng)。
為了達(dá)到這一目標(biāo),大家需要將包含動(dòng)畫效果的每個(gè)片段包含在一個(gè)<group>標(biāo)簽當(dāng)中。經(jīng)過(guò)修改的vector_drawable_cpu.xml版本將如下所示:
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="64dp"
- android:width="64dp"
- android:viewportHeight="600"
- android:viewportWidth="600" >
- <group
- android:name="cpu_box">
- <path
- android:name="cpu"
- android:fillColor="#000000"
- android:pathData="
- M341.087,157.478 c7.417,0,13.435,6.018,13.435,13.435 v170.174c0,7.417-6.018,13.435-13.435,13.435 H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913c0-7.417,6.018-13.435,13.435-13.435H341.087z
- M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785,0,35.826-16.041,35.826-35.826V157.478z "/>
- </group>
- <group
- android:name="bottom">
- <path
- android:name="wires_bottom"
- android:fillColor="#000000"
- android:pathData="
- M193.304,408.261V462h-17.913 v-53.739H193.304z
- M264.957,408.261V462h-17.914v-53.739H264.957z
- M300.783,408.261V462h-17.914v-53.739H300.783z
- M229.13,408.261 V462h-17.913v-53.739H229.13z
- M336.609,408.261V462h-17.914v-53.739H336.609z" />
- </group>
- <group android:name="top">
- <path
- android:name="wires_top"
- android:fillColor="#000000"
- android:pathData="
- M193.304,50v53.739h-17.913V50H193.304z
- M264.957,50 v53.739h-17.914V50H264.957z
- M300.783,50v53.739h-17.914V50H300.783z
- M229.13,50v53.739h-17.913V50H229.13z
- M336.609,50v53.739 h-17.914V50H336.609z " />
- </group>
- <group android:name="right">
- <path
- android:name="wires_right"
- android:fillColor="#000000"
- android:pathData="
- M408.261,318.695H462v17.914h-53.739V318.695z
- M408.261,247.043H462v17.914h-53.739V247.043z
- M408.261,211.217 H462v17.913h-53.739V211.217z
- M408.261,282.869H462v17.914h-53.739V282.869z
- M408.261,175.391H462v17.913h-53.739V175.391z" />
- </group>
- <group android:name="left">
- <path
- android:name="wires_left"
- android:fillColor="#000000"
- android:pathData="
- M50,318.695h53.739v17.914H50V318.695z
- M50,247.043h53.739v17.914H50V247.043z
- M50,211.217h53.739v17.913H50V211.217z
- M50,282.869 h53.739v17.914H50V282.869z
- M50,175.391h53.739v17.913H50V175.391z" />
- </group>
- </vector>
接下來(lái),我們需要為每個(gè)動(dòng)畫類型創(chuàng)建animator文件。在本次示例中,每組線路各使用一個(gè)animator,這就意味著共需要四個(gè)animator。以下代碼所示為上方線路的動(dòng)畫效果,大家還需要為下、左、右線路設(shè)定類似的效果。每個(gè)animator XML文件都被包含在了本項(xiàng)目的示例代碼當(dāng)中。
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:propertyName="translateY"
- android:valueType="floatType"
- android:valueFrom="0"
- android:valueTo="-10"
- android:repeatMode="reverse"
- android:repeatCount="infinite"
- android:duration="250" />
- </set>
如大家所見,propertyName被設(shè)定為translateY,這意味著該動(dòng)畫將沿Y軸方向移動(dòng)。而valueFrom與valueTo則控制著位移的起點(diǎn)與終點(diǎn)。通過(guò)將repeatMode設(shè)置為reverse而repeatCount設(shè)置為infinite,整個(gè)動(dòng)畫會(huì)一直循環(huán)下去,其效果則在VectorDrawable處體現(xiàn)出來(lái)。該動(dòng)畫的duration被設(shè)定為250,其時(shí)長(zhǎng)單位為毫秒。
為了將該動(dòng)畫應(yīng)用到自己的可繪制文件當(dāng)中,大家需要?jiǎng)?chuàng)建一個(gè)新的animated-vector XML文件,從而將這些animator分配給各VectorDrawable組。以下代碼的作用是創(chuàng)建該animated_cpu.xml文件。
- <?xml version="1.0" encoding="utf-8"?>
- <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/vector_drawable_cpu">
- <target
- android:animation="@animator/pulse_top"
- android:name="top" />
- <target
- android:animation="@animator/pulse_right"
- android:name="right" />
- <target
- android:animation="@animator/pulse_left"
- android:name="left" />
- <target
- android:animation="@animator/pulse_bottom"
- android:name="bottom" />
- </animated-vector>
當(dāng)所有必要的XML文件都已經(jīng)準(zhǔn)備完成后,大家就可以將animated_cpu.xml加入到ImageView當(dāng)中進(jìn)行顯示了。
- <ImageView
- android:id="@+id/cpu"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:src="@drawable/animated_cpu" />
要開始播放動(dòng)畫效果,大家需要從ImageView當(dāng)中獲取Animatable實(shí)例并調(diào)用start。
- ImageView mCpuImageView = (ImageView) findViewById( R.id.cpu );
- Drawable drawable = mCpuImageView.getDrawable();
- if (drawable instanceof Animatable) {
- ((Animatable) drawable).start();
- }
在start被調(diào)用之后,CPU圖形當(dāng)中的線路圖形就會(huì)開始移動(dòng)——整個(gè)過(guò)程只需要使用少量Java代碼即可實(shí)現(xiàn)。
3. Vector Drawables的變化方式
對(duì)于VectorDrawable來(lái)說(shuō),最常見的一種使用方式就是將一個(gè)圖形轉(zhuǎn)化至另一個(gè)圖形,例如操作欄中的圖標(biāo)由漢堡變成箭頭。要做到這一點(diǎn),源與目標(biāo)path二者都必須具備同樣的格式以保證元素?cái)?shù)量上的一致。在本次示例中,我們將如前文圖片所示嘗試將左箭頭轉(zhuǎn)化為右箭頭。
- <string name="left_arrow">M300,70 l 0,70 -70,-70 0,0 70,-70z</string>
- <string name="right_arrow">M300,70 l 0,-70 70,70 0,0 -70,70z</string>
接下來(lái),大家需要利用path為left_arrow建立一個(gè)初始drawable。在示例代碼中,我們將其命名為vector_drawable_left_arrow.xml。
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="64dp"
- android:width="64dp"
- android:viewportHeight="600"
- android:viewportWidth="600" >
- <path
- android:name="left_arrow"
- android:fillColor="#000000"
- android:pathData="@string/left_arrow"/>
- /vector>
CPU動(dòng)畫與這里提到的圖形變化示例之間,最主要的區(qū)別就體現(xiàn)在animator_left_right_arrow.xml文件當(dāng)中。
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <objectAnimator
- android:duration="1000"
- android:propertyName="pathData"
- android:valueFrom="@string/left_arrow"
- android:valueTo="@string/right_arrow"
- android:valueType="pathType"
- android:repeatMode="reverse"
- android:repeatCount="-1"/>
- </set>
大家可能已經(jīng)注意到了,valueFrom與valueTo兩項(xiàng)屬性會(huì)引用左箭頭與右箭頭的path數(shù)據(jù),valueType被設(shè)定為pathType而propertyName則被設(shè)定為pathData。當(dāng)以上設(shè)定完成之后,該animator將明確如何將一組path數(shù)據(jù)轉(zhuǎn)化為另一組。當(dāng)該animator結(jié)束之后,我們還需要利用新的animated-vector對(duì)象將VectorDrawable分配至objectAnimator。
- <?xml version="1.0" encoding="utf-8"?>
- <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/vector_drawable_left_arrow">
- <target
- android:name="left_arrow"
- android:animation="@animator/animator_left_right_arrows" />
- </animated-vector>
***,大家還需要將該動(dòng)畫drawable分配至ImageView,而后在自己的Java代碼中開始運(yùn)行。
- <ImageView
- android:id="@+id/left_right_arrow"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_below="@+id/cpu"
- android:src="@drawable/animated_arrow" />
- mArrowImageView = (ImageView) findViewById( R.id.left_right_arrow );
- drawable = mArrowImageView.getDrawable();
- if (drawable instanceof Animatable) {
- ((Animatable) drawable).start();
- }
總結(jié)
正如大家所見,VectorDrawable類非常易于使用而且允許開發(fā)者以自定義方式實(shí)現(xiàn)大量簡(jiǎn)單的動(dòng)畫效果。盡管VectorDrawable類目前只適用于運(yùn)行有Android 5.0以及更高版本的設(shè)備,但隨著更多設(shè)備開始支持Lollipop及其后續(xù)Android版本,其必將發(fā)揮更為重要的作用。