編輯:關於Android編程
兩年前寫書的時候,就在研究Android L提出的Vector,可研究下來發現,完全不具備兼容性,相信這也是它沒有被廣泛使用的一個原因,經過Google的不懈努力,現在Vector終於迎來了它的春天。在文章後面,會給出本文的Demo和效果圖,並開源在Github
Android 5.0發布的時候,Google提供了Vector的支持。Vector Drawable相對於普通的Drawable來說,有以下幾個好處:
Vector圖像可以自動進行適配,不需要通過分辨率來設置不同的圖片 Vector圖像可以大幅減少圖像的體積,同樣一張圖,用Vector來實現,可能只有PNG的幾十分之一 使用簡單,很多設計工具,都可以直接導出SVG圖像,從而轉換成Vector圖像 功能強大,不用寫很多代碼就可以實現非常復雜的動畫 成熟、穩定,前端已經非常廣泛的進行使用了Vector圖像剛發布的時候,是只支持Android 5.0+的,對於Android pre-L的系統來說,並不能使用,所以,可以說那時候的Vector並沒有什麼卵用。不過自從AppCompat 23.2之後,Google對p-View的Android系統也進行了兼容,也就是說,Vector可以使用於Android 2.1以上的所有系統,只需要引用com.android.support:appcompat-v7:23.2.0以上的版本就可以了,這時候,Vector應該算是迎來了它的春天。
首先,需要講解兩個概念——SVG和Vector。
SVG,即Scalable Vector Graphics 矢量圖,這種圖像格式在前端中已經使用的非常廣泛了,詳見WIKI:https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
Vector,在Android中指的是Vector Drawable,也就是Android中的矢量圖,詳見:https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html
因此,可以說Vector就是Android中的SVG實現,因為Android中的Vector並不是支持全部的SVG語法,也沒有必要,因為完整的SVG語法是非常復雜的,但已經支持的SVG語法已經夠用了,特別是Path語法,幾乎是Android中Vector的標配,詳細可以參考:http://www.w3.org/TR/SVG/paths.html
Android以一種簡化的方式對SVG進行了兼容,這種方式就是通過使用它的Path標簽,通過Path標簽,幾乎可以實現SVG中的其它所有標簽,雖然可能會復雜一點,但這些東西都是可以通過工具來完成的,所以,不用擔心寫起來會很復雜。
Path指令解析如下所示:
支持的指令:
M = moveto(M X,Y) :將畫筆移動到指定的坐標位置 L = lineto(L X,Y) :畫直線到指定的坐標位置 H = horizontal lineto(H X):畫水平線到指定的X坐標位置 V = vertical lineto(V Y):畫垂直線到指定的Y坐標位置 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 curveto(T ENDX,ENDY):映射 A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線 Z = closepath():關閉路徑使用原則:
坐標軸為以(0,0)為中心,X軸水平向右,Y軸水平向下 所有指令大小寫均可。大寫絕對定位,參照全局坐標系;小寫相對定位,參照父容器坐標系 指令和數據間的空格可以省略 同一指令出現多次可以只用一個注意,’M’處理時,只是移動了畫筆, 沒有畫任何東西。 它也可以在後面給出上同時繪制不連續線。
關於這些語法,開發者需要的並不是全部精通,而是能夠看懂即可,其它的都可以交給工具來實現。
要從一般使用的PNG圖像轉換到SVG圖像,對於設計師來說,並不是一件難事,因為大部分的設計工具(PS、Illustrator等等)都支持導出各種格式的圖像,如PNG、JPG,當然,也包括SVG,因此,設計師可以完全按照原有的方式進行設計,只是最後導出的時候,選擇SVG即可。
程序員不要求開發者都去學習使用這些設計工具,開發者可以利用一些工具,自己轉換一些比較基礎的圖像,http://inloop.github.io/svg2android/ 就是這樣一個非常牛逼的網站,可以在線將普通圖像轉換為Android Vector Drawable。如圖所示:
或者,還可以使用SVG的編輯器來進行SVG圖像的編寫,例如http://editor.method.ac/
利用Android Studio的Vector Asset,可以非常方便的創建Vector圖像,甚至可以直接通過本地的SVG圖像來生成Vector圖像,如圖所示:
進去之後,就可以生成VectZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcs28z/GjrMjnzbzL+cq+o7o8L3A+DQo8cD48aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160707/201607070912101011.png" title="\" />
Vector是在Android L中提出來的新概念,所以在剛開始的時候是只兼容L+的。
從Gradle Plugin 1.5開始,Google支持了一種兼容方式,即在Android L之上,使用Vector,而在L之下,則使用Gradle將Vector生成PNG圖像。
Android gradle plugin 1.5發布以後,加入了一個跟VectorDrawable有關的新功能。Android build tools 提供了另外一種解決兼容性的方案,如果編譯的版本是5.0之前的版本,那麼build tools 會把VectorDrawable生成對應的png圖片,這樣在5.0以下的版本則使用的是生成的png圖,而在5.0以上的版本中則使用VectorDrawable.在build.gradle添加generatedDensities配置,可以配置生成的png圖片的密度。
從AppCompat23.2開始,Google開始支持在低版本上使用Vector。
我們有很多方法能夠得到這些Vector,那麼如何使用它們呢,Android 5.0以上的使用就不講了,不太具有普遍代表性,我們從pre-L版本的兼容開始做起。
VectorDrawableCompat依賴於AAPT的一些功能,它能保持最近矢量圖使用的添加的屬性ID,以便他們可以被pre-L版本之前的引用。
在Android 5.0之前使用Vector,需要aapt來對資源進行一些處理,這一過程可以在aapt的配置中進行設置,如果沒有啟用這樣一個flag,那麼在5.0以下的設備上運行就會發生android.content.res.Resources$NotFoundException。
首先,你需要在項目的build.gradle腳本中,增加對Vector兼容性的支持,代碼如下所示:
使用Gradle Plugin 2.0以上:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
使用Gradle Plugin 2.0以下,Gradle Plugin 1.5以上:
android {
defaultConfig {
// Stops the Gradle plugin’s automatic rasterization of vectors
generatedDensities = []
}
// Flag to tell aapt to keep the attribute ids around
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
像前面提到的,這種兼容方式實際上是先關閉AAPT對pre-L版本使用Vector的妥協,即在L版本以上,使用Vector,而在pre-L版本上,使用Gradle生成相應的PNG圖片,generatedDensities這個數組,實際上就是要生成PNG的圖片分辨率的數組,使用appcompat後就不需要這樣了。
當然,最重要的還是添加appcompat的支持:
compile 'com.android.support:appcompat-v7:23.4.0'
同時,確保你使用的是AppCompatActivity而不是普通的Activity。
一個基本的Vector圖像,實際上也是一個xml文件,如下所示:
顯示如圖所示:
這裡需要解釋下這裡的幾個標簽:
android:width \ android:height:定義圖片的寬高 android:viewportHeight \ android:viewportWidth:定義圖像被劃分的比例大小,例如例子中的500,即把200dp大小的圖像劃分成500份,後面Path標簽中的坐標,就全部使用的是這裡劃分後的坐標系統。這樣做有一個非常好的作用,就是將圖像大小與圖像分離,後面可以隨意修改圖像大小,而不需要修改PathData中的坐標。
android:fillColor:PathData中的這些屬性就不詳細講了,與Canvas繪圖的屬性基本類似。有了靜態的Vector圖像,就可以在控件中使用了。
可以發現,這裡我們使用的都是普通的ImageView,好像並不是AppcomatImageView,這是因為使用了Appcomat後,系統會自動把ImageView轉換為AppcomatImageView。
對於ImageView這樣的控件,要兼容Vector圖像,只需要將之前的android:src屬性,換成app:srcCompat即可,示例代碼如下所示:
在代碼中設置的話,代碼如下所示:
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageResource(R.drawable.vector_image);
setBackgroundResource也是可以設置Vector的API
Button並不能直接使用app:srcCompat來使用Vector圖像,需要通過Selector來進行使用,首先,創建兩個圖像,用於Selector的兩個狀態,代碼如下所示:
selector1.xml
selector2.xml
selector.xml
-
-
非常簡單,只是把普通的Selector中的圖像換成了Vector圖像而已,接下來,在Button中使用這個Selector即可:
然後運行,如果你認為可以運行,那就是太天真了,都說了是兼容,怎麼能沒有坑呢,這裡就是一個坑……
這個坑實際上是有歷史淵源的,Google的一位開發者在博客中寫到:
First up, this functionality was originally released in 23.2.0, but then we found some memory usage and Configuration updating issues so we it removed in 23.3.0. In 23.4.0 (technically a fix release) we’ve re-added the same functionality but behind a flag which you need to manually enable.
實際上,他們的這個改動,就影響了類似DrawableContainers(DrawableContainers which reference other drawables resources which contain only a vector resource)這樣的類,它的一個典型,就是Selector(StateListDrawable也是)。這個開發者在文中提到的flag,就是下面的這段代碼,放在Activity的前面就可以了:
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
開啟這個flag後,你就可以正常使用Selector這樣的DrawableContainers了。同時,你還開啟了類似android:drawableLeft這樣的compound drawable的使用權限,以及RadioButton的使用權限,以及ImageView’s src屬性。
RadioButton的Button同樣可以定義,代碼如下所示:
動態Vector才是Android Vector Drawable的精髓所在
動態的Vector需要通過animated-vector標簽來進行實現,它就像一個粘合劑,將控件與Vector圖像粘合在了一起,一個基礎的animated-vector代碼如下所示:
實際上這裡面只有兩個重點是需要關注的,XXXXX1和XXXXX2。一個具體的示例如下所示:
這裡表示目標圖像是drawable/ic_arrow,對left、right分別使用了anim_left、anim_right動畫。這裡的name屬性,就是在靜態Vector圖像中group或者path標簽的name屬性。
animated-vector標簽在現在的Android Studio中實際上是會報錯的,但這個並不影響編譯和運行,屬於Android Studio的Bug。
XXXXX1是目標Vector圖像,也就是靜態的Vector圖像,例如:
可以發現,這裡的Vector圖像比之前我們看見的要多了一個group標簽。group標簽的作用有兩個:
對Path進行分組,由於我們後面需要針對Path進行動畫,所以可以讓具有同樣動畫效果的Path在同一個Group中 拓展動畫效果,單個的path標簽是沒有translateX和translateY屬性的,因此無法使用屬性動畫來控制path translateY,而group標簽是有的,所以我們需要先將相關的path標簽元素包裹在一個個的group標簽中.XXXXX2實際上就是模板要實現的動畫,動畫效果實際上就是基礎的屬性動畫,例如:
anim_left.xml
anim_right.xml
ImageView imageView = (ImageView) findViewById(R.id.iv);
AnimatedVectorDrawableCompat animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(
this, R.drawable.square_anim
);
imageView.setImageDrawable(animatedVectorDrawableCompat);
((Animatable) imageView.getDrawable()).start();
一說到兼容,就不得不提到坑,幾乎所有的為了兼容而做的改動,都會留下一些不可填滿的坑,動態Vector動畫也不例外,雖然Google已經對Vector圖像進行了Android 2.1以上的兼容,但對於動態Vector動畫,還是有很多限制的,例如:
Path Morphing,即路徑變換動畫,在Android pre-L版本下是無法使用的。 Path Interpolation,即路徑插值器,在Android pre-L版本只能使用系統的插值器,不能自定義。 Path Animation,即路徑動畫,這個一般使用貝塞爾曲線來代替,所以沒有太大影響。除了在低版本上的兼容性問題,在L版本以上,也存在兼容性問題,即繼承了AppCompatActivity的界面,如果直接設置ImageView的srcCompat,那麼Path Morphing動畫是無法生效的,因為默認的AppCompatActivity已經默認使用ImageViewCompat給轉換了,但是AnimatedVectorDrawableCompat是不支持Path Morphing動畫的,所以,在AppCompatActivity界面裡面就無效了。
解決辦法很簡單,即使用代碼來給ImageView添加動畫:
ImageView imageView = (ImageView) view;
AnimatedVectorDrawable morphing = (AnimatedVectorDrawable) getDrawable(morphing);
imageView.setImageDrawable(morphing);
if (morphing != null) {
morphing.start();
}
注意不要使用AnimatedVectorDrawableCompat即可。
開發者有時候為了代碼簡潔可能會把Vector圖像中的pathData放到string.xml中,然後在Vector圖像中引用string。
但這種方式如果通過生成png來兼容5.0以下機型的話,會報pathData錯誤,編譯器不會去讀取string.xml,只能把pathData寫到Vector圖像中,動畫文件中也是一樣,這也是為了兼容做出的犧牲嗎,不得而知。
其它非常奇怪、詭異、不能理解的兼容性問題,只能通過版本文件夾的方式來進行兼容了,例如drawable-v21和drawable,分別創建兩個文件名相同的資源在兩個文件夾下,這樣在21以上版本,會使用drawable-v21的資源,而其它會使用drawable下的資源。
所謂Vector動畫進階,實際上就是在利用ObjectAnimator的一些屬性,特別是trimPathStart、trimPathEnd這兩個針對Vector的屬性(要注意pathData屬性不兼容pre-L)。
這兩個屬性的官方文檔如下所示:
android:trimPathStart
The fraction of the path to trim from the start, in the range from 0 to 1.
android:trimPathEnd
The fraction of the path to trim from the end, in the range from 0 to 1.
android:trimPathOffset
Shift trim region (allows showed region to include the start and end), in the range from 0 to 1.
其實很簡單,就是一個圖像的截取,設置一個比例即可,即當前繪制多少比例的圖像,其余部分不繪制,Start和End分別就是從PathData的Start和End開始算,大家參考幾個例子就能理解了。
Path Morph動畫是Vector動畫的一個高級使用,說到底,也就是兩個PathData的轉換,但是這種轉換並不是隨心所欲的,對於兩個PathData,它們能進行Path Morph的前提是,它們具有相同個數的關鍵點,即兩個路徑的變換,只是關鍵點的坐標變化,掌握了這一個基本原理,實現Path Morph就非常容易了。
在Github上我開源了一個Vector的動畫Demo庫,地址如下所示:
https://github.com/xuyisheng/VectorDemo
這個Demo分為兩部分,一部分是可以兼容Android pre-L版本和L+版本的Vector動畫,另一部分(通過Actionbar的按鈕切換)是只能兼容L+的Vector動畫。
每個Vector動畫,基本都包含四部分內容,即:
Vector:圖像資源 Animated-vector:動畫、圖像粘合劑 ObjectAnimator:動畫資源 代碼:啟動動畫每個Vector動畫通過這四個部分去進行分析,就非常清晰了。
這裡展示下Demo的效果圖:
https://github.com/jpuderer/AnimatedButton
ProgressBar作用:當應在後台執行時,前台界面不會有任何信息,這時用戶根本不知道程序是否在執行,以及執行進度等,因些需要使用進度條來提示程序執行的進度.在Andr
從本篇博客開始,我們開始分析PKMS的構造函數,看看PKMS到底是如何解析和管理手機中APK的信息的。由於PKMS的構造函數較長,我們會分段進行研究。public Pac
1.Toast源碼分析老規矩,我們先去看Toast的源碼。Toast有兩種顯示布局方式,一種最常見調用Toast.makeText() ,看源碼是這樣寫的pu
在本篇文章中給出了三種實現日間/夜間模式切換的方案,三種方案綜合起來可能導致文章的篇幅過長,請耐心閱讀。 1、使用 setTheme