Android Vector適配

官網(wǎng)文檔

簡(jiǎn)述

矢量圖的主要優(yōu)點(diǎn)是可以無損的拉伸和縮放敌蜂,而且本身的是一個(gè)xml文件,這樣可以避免開發(fā)時(shí)再使用多套圖片腾誉,減少APK大小,尤其在一些動(dòng)畫效果上節(jié)省體積更明顯。另外可以通過fillColor或者android:tint動(dòng)態(tài)改變圖片顏色肄梨,方便開發(fā)。

Android Studio里面內(nèi)置了一些矢量圖秸脱,可以通過new->vector assets選擇一些圖標(biāo)姨蝴。
vector assetes.png

默認(rèn)適配

但是Android5.0(API21)以上才正式支持矢量圖,5.0以下的手機(jī)需要通過AnimatedVectorDrawableVectorDrawable適配策幼,不然系統(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启昧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市劈伴,隨后出現(xiàn)的幾起案子密末,更是在濱河造成了極大的恐慌,老刑警劉巖跛璧,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件严里,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡追城,警方通過查閱死者的電腦和手機(jī)刹碾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來座柱,“玉大人迷帜,你說我怎么就攤上這事∩矗” “怎么了戏锹?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)火诸。 經(jīng)常有香客問我锦针,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任伞插,我火速辦了婚禮割粮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘媚污。我一直安慰自己舀瓢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布耗美。 她就那樣靜靜地躺著京髓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪商架。 梳的紋絲不亂的頭發(fā)上堰怨,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音蛇摸,去河邊找鬼备图。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赶袄,可吹牛的內(nèi)容都是我干的揽涮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼饿肺,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蒋困!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起敬辣,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤雪标,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溉跃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體村刨,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年撰茎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嵌牺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乾吻,死狀恐怖髓梅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绎签,我是刑警寧澤枯饿,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站诡必,受9級(jí)特大地震影響奢方,放射性物質(zhì)發(fā)生泄漏搔扁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一蟋字、第九天 我趴在偏房一處隱蔽的房頂上張望稿蹲。 院中可真熱鬧,春花似錦鹊奖、人聲如沸苛聘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽设哗。三九已至,卻和暖如春两蟀,著一層夾襖步出監(jiān)牢的瞬間网梢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工赂毯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留战虏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓党涕,卻偏偏與公主長(zhǎng)得像烦感,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子遣鼓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容