res下的 drawable 是如何解析成 Drawable 對象?

Drawable 可以方便的作為View的背景使用灌闺,也可以做為 ListView 的 divider 等等骤公。在res/drawable下通過xml可以很方便的定義一個Drawable,顯然我們的 View 是無法直接使用這個 xml 文件的上陕,它必須先解析成 Drawable 對象才能供我們的 View 顯示桩砰。那么這個xml文件是如何解析為 Drawable 對象的呢?

Drawable簡單使用

在 res/drawable/新建一個 bg.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/black" />
</shape>

有了這個 bg.xml 我們就可以為 View 指定一個background屬性了释簿,當然也可以在 java 代碼中設置:

Drawable d = getResources().getDrawable(R.drawable.bg);
view.setBackground(d);

實際上在 layout 中指定 background屬性最終也會走上面的代碼亚隅。接下來分析下Resources#getDrawable(int id)這個方法。這個方法負責將給定資源 id 的 drawable 文件解析成 Drawable 對象庶溶。

Resources#getDrawable(int id)

Resources#getDrawable(int id) 最終會調用 getDrawable(int id,Theme theme) 煮纵,我們看下這個方法:

public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) throws NotFoundException {
    TypedValue value;
    synchronized (mAccessLock) {
        value = mTmpValue;
        if (value == null) {
            value = new TypedValue();
        } else {
            mTmpValue = null;
        }
        getValue(id, value, true);
    }
    // 傳入 id, 返回 Drawable, 重點關注
    final Drawable res = loadDrawable(value, id, theme);
    synchronized (mAccessLock) {
        if (mTmpValue == null) {
            mTmpValue = value;
        }
    }
    return res;
}

首先 getValue(value, id, theme)方法先檢查指定id的xml文件是否存在。這個方法可能會對TypeValue進行一些賦值偏螺。比如后面用到的 typeValue.string應該就是制定id的文件名(帶后綴的)行疏。

然后調用loadDrawable(value, id, theme)去獲取 Drawable對象。
顯然重點方法是loadDrawable(value, id, theme)套像。跟進去這個方法:

Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
    // 省略...
    Drawable dr;
    if (cs != null) {
        dr = cs.newDrawable(this);
    } else if (isColorDrawable) {
        dr = new ColorDrawable(value.data);
    } else {
        dr = loadDrawableForCookie(value, id, null);
    }
    // 省略...
}

省略部分源碼酿联,我們重點關注 id 傳入哪個方法,該方法返回值是不是 Drawable 對象夺巩。如果是贞让,就應該重點關注。

根據(jù)這個規(guī)則猜測 loadDrawableForCookie 可能是我們想要尋找的方法柳譬,跟進去看下:

private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
    // value.string: xml 文件名
    if (value.string == null) {
        throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
                + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
    }

    final String file = value.string.toString();

    // false ,不看
    if (TRACE_FOR_MISS_PRELOAD) {
        // Log only framework resources
        if ((id >>> 24) == 0x1) {
            final String name = getResourceName(id);
            if (name != null) {
                Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
                        + ": " + name + " at " + file);
            }
        }
    }

    if (DEBUG_LOAD) {
        Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
    }

    final Drawable dr;

    Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
    try {
        // file 為我們的 drawable 文件喳张,比如 bg.xml
        if (file.endsWith(".xml")) {
            final XmlResourceParser rp = loadXmlResourceParser(
                    file, id, value.assetCookie, "drawable");
            dr = Drawable.createFromXml(this, rp, theme);
            rp.close();
        } else {
            final InputStream is = mAssets.openNonAsset(
                    value.assetCookie, file, AssetManager.ACCESS_STREAMING);
            dr = Drawable.createFromResourceStream(this, value, is, file, null);
            is.close();
        }
    } catch (Exception e) {
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        final NotFoundException rnf = new NotFoundException(
                "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
        rnf.initCause(e);
        throw rnf;
    }
    Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

    return dr;
}

重點在 try 語句,if (file.endsWith(".xml"))條件成立征绎,接著執(zhí)行loadXmlResourceParser去獲取一個xml Parser解析器蹲姐,注意到這里傳入了我們的 id磨取。緊接著執(zhí)行/*Drawable*/ dr = Drawable.createFromXml(/*Resources*/this, rp, theme) 方法。這是一個靜態(tài)方法柴墩,返回的是 Drawable 對象忙厌,并且這個 Drawable 最終會作為 loadDrawableForCookie 的返回值,然后一步一步返回到最開始的Resources#getDrawable方法江咳。到此逢净,我們就知道Drawable.createFromXml(Resources r, XmlPullParser parser, Theme theme) 完成了 Drawable 對象的解析工作。趕緊跟進去看下:

public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
        throws XmlPullParserException, IOException {
    AttributeSet attrs = Xml.asAttributeSet(parser);

    int type;
    while ((type=parser.next()) != XmlPullParser.START_TAG &&
            type != XmlPullParser.END_DOCUMENT) {
        // Empty loop
    }

    if (type != XmlPullParser.START_TAG) {
        throw new XmlPullParserException("No start tag found");
    }

    Drawable drawable = createFromXmlInner(r, parser, attrs, theme);

    if (drawable == null) {
        throw new RuntimeException("Unknown initial tag: " + parser.getName());
    }

    return drawable;
}

重點在createFromXmlInner(r, parser, attrs, theme)歼指,繼續(xù)跟進:

public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
        Theme theme) throws XmlPullParserException, IOException {
    final Drawable drawable;
    // drawable.xml 下的跟節(jié)點
    final String name = parser.getName();
    switch (name) {
        case "selector":
            drawable = new StateListDrawable();
            break;
        case "animated-selector":
            drawable = new AnimatedStateListDrawable();
            break;
        case "level-list":
            drawable = new LevelListDrawable();
            break;
        case "layer-list":
            drawable = new LayerDrawable();
            break;
        case "transition":
            drawable = new TransitionDrawable();
            break;
        case "ripple":
            drawable = new RippleDrawable();
            break;
        case "color":
            drawable = new ColorDrawable();
            break;
        case "shape":
            drawable = new GradientDrawable();
            break;
        case "vector":
            drawable = new VectorDrawable();
            break;
        case "animated-vector":
            drawable = new AnimatedVectorDrawable();
            break;
        case "scale":
            drawable = new ScaleDrawable();
            break;
        case "clip":
            drawable = new ClipDrawable();
            break;
        case "rotate":
            drawable = new RotateDrawable();
            break;
        case "animated-rotate":
            drawable = new AnimatedRotateDrawable();
            break;
        case "animation-list":
            drawable = new AnimationDrawable();
            break;
        case "inset":
            drawable = new InsetDrawable();
            break;
        case "bitmap":
            drawable = new BitmapDrawable();
            break;
        case "nine-patch":
            drawable = new NinePatchDrawable();
            break;
        default:
            throw new XmlPullParserException(parser.getPositionDescription() +
                    ": invalid drawable tag " + name);

    }
    drawable.inflate(r, parser, attrs, theme);
    return drawable;
}

到這里瞬間恍然大悟爹土。

這個方法會獲取xml定義的根節(jié)點,根據(jù)根節(jié)點構造出相應的 Drawable對象踩身,然后調用drawable.inflate(r, parser, attrs, theme)方法把xml定義的一些屬性設置到drawable對象上胀茵。如果 Drawable 的子類有自己的屬性,那么就可以重寫 inflate 這個方法來解析特有的屬性挟阻。

另外琼娘,注意到,在Drawable.createFromXmlInner方法附鸽,發(fā)現(xiàn)我們在xml 定義的 shape 實際上是 GradientDrawable脱拼,而不是 ShapeDrawable

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末坷备,一起剝皮案震驚了整個濱河市熄浓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌省撑,老刑警劉巖赌蔑,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異丁侄,居然都是意外死亡惯雳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門鸿摇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劈猿,你說我怎么就攤上這事拙吉。” “怎么了揪荣?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵筷黔,是天一觀的道長。 經常有香客問我仗颈,道長佛舱,這世上最難降的妖魔是什么椎例? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮请祖,結果婚禮上订歪,老公的妹妹穿的比我還像新娘。我一直安慰自己肆捕,他們只是感情好刷晋,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著慎陵,像睡著了一般眼虱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上席纽,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天捏悬,我揣著相機與錄音,去河邊找鬼润梯。 笑死过牙,一個胖子當著我的面吹牛,可吹牛的內容都是我干的仆救。 我是一名探鬼主播抒和,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼彤蔽!你這毒婦竟也來了摧莽?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤顿痪,失蹤者是張志新(化名)和其女友劉穎镊辕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚁袭,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡征懈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了揩悄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卖哎。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖删性,靈堂內的尸體忽然破棺而出亏娜,到底是詐尸還是另有隱情,我是刑警寧澤蹬挺,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布维贺,位于F島的核電站,受9級特大地震影響巴帮,放射性物質發(fā)生泄漏溯泣。R本人自食惡果不足惜虐秋,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望垃沦。 院中可真熱鬧客给,春花似錦、人聲如沸栏尚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽译仗。三九已至抬虽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纵菌,已是汗流浹背阐污。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咱圆,地道東北人笛辟。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像序苏,于是被迫代替她去往敵國和親手幢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,107評論 25 707
  • //通過獲得資源文件進行設置忱详。根據(jù)不同的情況R.color.red也可以是R.string.red或者R.draw...
    gogoingmonkey閱讀 1,942評論 0 2
  • 1围来、Drawable 簡介 Drawable——可簡單理解為可繪制物,表示一些可以繪制在 Canvas 上的對象匈睁。...
    牧秦丶閱讀 14,810評論 0 15
  • 沒關系航唆,就這樣離去胀蛮,,糯钙,粪狼,, 在火車站任岸,遇到一個姑娘鸳玩,穿著厚厚的羽絨服,一雙紅色的馬丁靴演闭,應該是在打車。面對...
    人花你們選閱讀 257評論 0 1
  • 離春節(jié)的日子越來越近了窝革,陪我大半年的媽媽要回老家過年了。突然很不舍吕座。 晚上虐译,我練瑜伽,媽媽就坐在床頭看著吴趴,默默的漆诽,...
    小貓說法閱讀 286評論 0 4