如何使用Android的VectorDrawable類繪制矢量圖
譯文繪制矢量圖形非難事——如何使用Android的VectorDrawable類
內容概述
盡管Android系統并不能夠直接支持SVG(即可縮放矢量圖形),但Lollipop版本卻引入了一個名為VectorDrawable的新類,其允許設計人員及開發人員以純代碼方式生成類似的繪制效果。
在今天的文章中,我們將共同學習如何利用XML文件創建一個VectorDrawable,并將其以動畫方式顯示在自己的項目當中。這項功能只能在運行有Android 5.0或者更高版本的設備上實現,而且目前還不具備任何支持庫實現。本篇教程中的相關源文件可以通過GitHub網站獲取。
1. 創建Vector Drawable
從相似角度來看,VectorDrawable與標準SVG圖形都是利用path值繪制完成的。不過如何利用SVG path繪制圖形并不在本篇文章的探討范圍之內,大家可以點擊此處從W3C網站處獲取必要的說明資料。在本文當中,我們只需要了解到path標簽的作用是進行圖形繪制即可。讓我們首先從SVG文件入手,看看以下圖形是如何被繪制出來的:
這一圖形共由五個主要部分所組成:
一個圓角四邊形作為CPU主體,該四邊形由兩條拱狀弧線構成。
四組各自包含五根線條的圖形,用于充當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>
雖然看起來有點繁雜,但大家其實用不著糾結于以上代碼的具體含義,而且這完全不會影響到我們接下來要進行的VectorDrawable繪制工作。不過需要強調的是,我將前面提到的五大圖形組成部分在代碼中作為獨立的區塊來處理,這是為了增強代碼內容的可讀性。
首先,我們需要利用兩條拱形弧線來繪制出圓角四邊形,而在接下來的內容中我們會探討如何分別表現出上、下、左、右四個方位的外延線條。為了將上述SVG代碼轉化為VectorDrawable,大家首先需要在XML當中定義vector對象。以下代碼提取自本篇文章示例代碼當中的vector_drawable_cpu.xml文件。
在此之后,大家可以向其中添加path數據。下列代碼同樣被拆分成了五個不同的path標簽而非將其作為整體處理,這當然也是為了保證內容的可讀性。
- <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>
正如大家所見,每個path片段都只需要利用pathData屬性進行繪制。現在我們可以將VectorDrawable XML文件作為一個可繪制對象納入到標準ImageView當中,而且其能夠根據應用程序的實際需要任意進行尺寸縮放——完全不需要再修改任何Java代碼。
2. 為Vector Drawables添加動畫效果
現在我們已經了解了如何以純代碼方式創建圖形,接下來要做的是找點樂子——為其添加動畫效果。在以下動畫中,大家會發現作為延伸線路的各組線條會不斷指向并遠離CPU本體進行移動。
為了達到這一目標,大家需要將包含動畫效果的每個片段包含在一個<group>標簽當中。經過修改的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>
接下來,我們需要為每個動畫類型創建animator文件。在本次示例中,每組線路各使用一個animator,這就意味著共需要四個animator。以下代碼所示為上方線路的動畫效果,大家還需要為下、左、右線路設定類似的效果。每個animator XML文件都被包含在了本項目的示例代碼當中。
- <?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被設定為translateY,這意味著該動畫將沿Y軸方向移動。而valueFrom與valueTo則控制著位移的起點與終點。通過將repeatMode設置為reverse而repeatCount設置為infinite,整個動畫會一直循環下去,其效果則在VectorDrawable處體現出來。該動畫的duration被設定為250,其時長單位為毫秒。
為了將該動畫應用到自己的可繪制文件當中,大家需要創建一個新的animated-vector XML文件,從而將這些animator分配給各VectorDrawable組。以下代碼的作用是創建該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>
當所有必要的XML文件都已經準備完成后,大家就可以將animated_cpu.xml加入到ImageView當中進行顯示了。
- <ImageView
- android:id="@+id/cpu"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:src="@drawable/animated_cpu" />
要開始播放動畫效果,大家需要從ImageView當中獲取Animatable實例并調用start。
- ImageView mCpuImageView = (ImageView) findViewById( R.id.cpu );
- Drawable drawable = mCpuImageView.getDrawable();
- if (drawable instanceof Animatable) {
- ((Animatable) drawable).start();
- }
在start被調用之后,CPU圖形當中的線路圖形就會開始移動——整個過程只需要使用少量Java代碼即可實現。
3. Vector Drawables的變化方式
對于VectorDrawable來說,最常見的一種使用方式就是將一個圖形轉化至另一個圖形,例如操作欄中的圖標由漢堡變成箭頭。要做到這一點,源與目標path二者都必須具備同樣的格式以保證元素數量上的一致。在本次示例中,我們將如前文圖片所示嘗試將左箭頭轉化為右箭頭。
- <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>
接下來,大家需要利用path為left_arrow建立一個初始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動畫與這里提到的圖形變化示例之間,最主要的區別就體現在animator_left_right_arrow.xml文件當中。
- <?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>
大家可能已經注意到了,valueFrom與valueTo兩項屬性會引用左箭頭與右箭頭的path數據,valueType被設定為pathType而propertyName則被設定為pathData。當以上設定完成之后,該animator將明確如何將一組path數據轉化為另一組。當該animator結束之后,我們還需要利用新的animated-vector對象將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>
***,大家還需要將該動畫drawable分配至ImageView,而后在自己的Java代碼中開始運行。
- <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();
- }
總結
正如大家所見,VectorDrawable類非常易于使用而且允許開發者以自定義方式實現大量簡單的動畫效果。盡管VectorDrawable類目前只適用于運行有Android 5.0以及更高版本的設備,但隨著更多設備開始支持Lollipop及其后續Android版本,其必將發揮更為重要的作用。