簡(jiǎn)述
矢量圖的主要優(yōu)點(diǎn)是可以無損的拉伸和縮放敌蜂,而且本身的是一個(gè)xml文件,這樣可以避免開發(fā)時(shí)再使用多套圖片腾誉,減少APK大小,尤其在一些動(dòng)畫效果上節(jié)省體積更明顯。另外可以通過fillColor或者android:tint動(dòng)態(tài)改變圖片顏色肄梨,方便開發(fā)。
默認(rèn)適配
但是Android5.0(API21)以上才正式支持矢量圖,5.0以下的手機(jī)需要通過AnimatedVectorDrawable和VectorDrawable適配策幼,不然系統(tǒng)默認(rèn)會(huì)為矢量圖生成每一套分辨率下的圖片(/build/generated/res/pngs)邑时,原來的矢量圖放到了drawable-anyapi-v21目錄下√亟悖可以看到自動(dòng)生成的圖片質(zhì)量很差晶丘,這樣就失去了使用矢量圖的意義。
兼容方式
想要使用兼容模式唐含,需要在gradle中添加如下配置
//For Gradle Plugin 2.0+
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
//For Gradle Plugin 1.5 or below
android {
defaultConfig {
// Stops the Gradle plugin’s automatic rasterization of vectors
generatedDensities = []
}
// Flag notifies aapt to keep the attribute IDs around
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
加上上述配置后重新clean編譯浅浮,generated就不再自動(dòng)生成相應(yīng)的png文件了,引用vector的地方使用
//使用android:src="@drawable/ic_add"捷枯,低版本手機(jī)會(huì)crash滚秩。
app:srcCompat="@drawable/ic_add"
這時(shí)候如果手機(jī)API小于24,而Vector中使用<gradient>標(biāo)簽APP會(huì)crash淮捆,只能避免在低版本中使用該標(biāo)簽叔遂,只將其添加在drawable-v24包下.
特殊標(biāo)簽
這里有幾個(gè)xml中的標(biāo)簽和屬性與Android版本有關(guān)。
首先介紹一下"vectorDrawables.useSupportLibrary = true"争剿,這個(gè) 是矢量圖的兼容庫已艰,使用該庫項(xiàng)目編譯時(shí)不會(huì)自動(dòng)生成任何png,如果加了該庫并且xml中有低版本不支持的屬性或者標(biāo)簽蚕苇,實(shí)際測(cè)試下來在低版本手機(jī)上有的xml會(huì)有以下兩種問題:(1)使用app:srcCompat引用圖片可能顯示不正常哩掺。(2)使用drawableTop等屬性引用該xml應(yīng)用直接crash。
以下介紹的需要兼容的標(biāo)簽或者屬性都要分使用"vectorDrawables.useSupportLibrary = true"和不使用兩種情況去分析涩笤。
- <android:fillColor="@color/xxx"> 這里引用資源需要API 21支持嚼吞,直接使用#123456這種則可以。(1)引用兼容庫可以解決該屬性的兼容問題 (2)不引用兼容庫蹬碧,如果minAPI小于21并且引用資源就不能生成png圖片舱禽,編譯會(huì)失敗。
- <gradient>標(biāo)簽只能用于API 24及以上恩沽。 (1)引用兼容庫誊稚,編譯時(shí)不會(huì)有問題,但是在低版本上運(yùn)行時(shí)如果沒用兼容方法引用會(huì)直接crash,比如drawableTop引用里伯。(2)不引用兼容庫城瞎,該標(biāo)簽可以正常生成png圖片,xml只用在drawable-anydpi-v24中疾瓮。
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #12: invalid drawable tag gradient
- <android:fillType="">只支持API 24以上脖镀。這個(gè)屬性在絕大部分情況下都可以安全刪除,不會(huì)影響圖片顯示狼电,但個(gè)別圖片也會(huì)被改變顯示蜒灰。(1) 引用兼容庫時(shí)編譯運(yùn)行正常,但實(shí)測(cè)在低版本上該屬性會(huì)被忽略肩碟。(2)不引用兼容庫編譯生成png時(shí)會(huì)失敗强窖,需要?jiǎng)h除。
兼容原理
使用app:srcCompat或者動(dòng)態(tài)使用setImageResource()可以讓低版本使用矢量圖腾务,我們可以在v7包中的values.xml中找到srcCompat的定義
<declare-styleable name="AppCompatImageView">
....
<!-- Sets a drawable as the content of this ImageView. Allows the use of vector drawable
when running on older versions of the platform. -->
<attr format="reference" name="srcCompat"/>
....
</declare-styleable>
之后我們?cè)贏ppCompatImageView控件中可以看到使用app:srcCompat或者setImageResource()最終都會(huì)在AppCompatImageHelper中調(diào)到如下方法
Drawable d = AppCompatResources.getDrawable(mView.getContext(), resId);
最終完成低版本使用矢量圖在VectorDrawableCompat完成毕骡,如果API < 24就使用XmlPullParser解析節(jié)點(diǎn)構(gòu)建drawable削饵,否則直接通過ResourcesCompat獲取岩瘦。
synchronized Drawable getDrawable(@NonNull Context context, @DrawableRes int resId, boolean failIfNotKnown) {
//會(huì)加載一個(gè)測(cè)試用的vector,如果加載失敗則throw一個(gè)未初始化矢量圖的exception
checkVectorDrawableSetup(context);
//通過委托模式加載圖片
Drawable drawable = this.loadDrawableFromDelegates(context, resId);
.....
return drawable;
}
//loadDrawableFromDelegates方法中使用XmlPullParser獲得AttributeSet
AppCompatDrawableManager.InflateDelegate delegate = (AppCompatDrawableManager.InflateDelegate)this.mDelegates.get(tagName);
if (delegate != null) {
//在VectorDrawableCompat的inflate中中通過解析各個(gè)節(jié)點(diǎn)窿撬,繪制drawable
dr = delegate.createFromXmlInner(context, parser, attrs, context.getTheme());
}
private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
VectorDrawableCompat.VectorDrawableCompatState state = this.mVectorState;
VectorDrawableCompat.VPathRenderer pathRenderer = state.mVPathRenderer;
boolean noPathTag = true;
ArrayDeque<VectorDrawableCompat.VGroup> groupStack = new ArrayDeque();
groupStack.push(pathRenderer.mRootGroup);
int eventType = parser.getEventType();
for(int innerDepth = parser.getDepth() + 1; eventType != 1 && (parser.getDepth() >= innerDepth || eventType != 3); eventType = parser.next()) {
String tagName;
if (eventType == 2) {
tagName = parser.getName();
VectorDrawableCompat.VGroup currentGroup = (VectorDrawableCompat.VGroup)groupStack.peek();
if ("path".equals(tagName)) {
VectorDrawableCompat.VFullPath path = new VectorDrawableCompat.VFullPath();
path.inflate(res, attrs, theme, parser);
currentGroup.mChildren.add(path);
if (path.getPathName() != null) {
pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
}
noPathTag = false;
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if ("clip-path".equals(tagName)) {
VectorDrawableCompat.VClipPath path = new VectorDrawableCompat.VClipPath();
path.inflate(res, attrs, theme, parser);
currentGroup.mChildren.add(path);
if (path.getPathName() != null) {
pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
}
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if ("group".equals(tagName)) {
VectorDrawableCompat.VGroup newChildGroup = new VectorDrawableCompat.VGroup();
newChildGroup.inflate(res, attrs, theme, parser);
currentGroup.mChildren.add(newChildGroup);
groupStack.push(newChildGroup);
if (newChildGroup.getGroupName() != null) {
pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup);
}
state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
}
} else if (eventType == 3) {
tagName = parser.getName();
if ("group".equals(tagName)) {
groupStack.pop();
}
}
}
if (noPathTag) {
throw new XmlPullParserException("no path defined");
}
}
其它
從上面可以推測(cè)如果Activity使用的是AppCompatActivity, ImageView會(huì)被替換成AppCompatImageView启昧。