源碼分析Android 中ImageView的設置src與background繪制流程

本文原文發(fā)表于vcmo博客 by Jie

眾所周知ImageView可以通過src和Background兩種方式設置顯示資源。和大家一起通過源碼來了解兩種屬性的繪制流程有何不同。

熱身案例

先上兩段小代碼:

    <ImageView
        android:layout_width="200dp"
        android:layout_height="300dp"
        android:background="@mipmap/ic_launcher"/>
    <ImageView
        android:layout_width="200dp"
        android:layout_height="300dp"
        android:src="@mipmap/ic_launcher"/>


21-49-56_363701.jpg

android:background="@mipmap/ic_launcher"



21-33-45.jpg

android:src="@mipmap/ic_launcher"


例子中壳影,可以看到ImageView通過設置Background會將圖片資源拉伸至控件的寬高讶迁。通過src設置并不會有這樣的現(xiàn)象羞芍。這是為什么呢?下面就同大家一起在源碼中找到答案糖驴。
ImageView 是View的直接子類洪唐,我們先從ImageView的源碼入手跨琳。

src與background兩種屬性設置的方法入手

setImageResource()方法入手src屬性

在ImageView的源碼中我們很容易能找到設置ImageResource的方法:


21-49-56.jpg

方法很短

  1. 首先將兩個全局變量的值賦給了局部變量。
  2. 調(diào)用了updateDrawable(null)桐罕,更新Drawable脉让,并將null作為參數(shù)桂敛。對,是用來初始化的溅潜。
  3. 將我們傳入的resId賦值給全局變量mResource术唬。
  4. 調(diào)用resolveUri();名稱的意思是解析資源
  5. 根據(jù)新的寬高是否等于舊的值滚澜,判斷是否請求重新計算布局粗仓。
  6. invalidate(),重繪

通過上面方法,找到了resolveUri()方法,看看里面做了什么设捐。

    private void resolveUri() {
    
    ···
    
        Drawable d = null;

        if (mResource != 0) {
            try {
                d = mContext.getDrawable(mResource);
            } catch (Exception e) {
                Log.w("ImageView", "Unable to find resource: " + mResource, e);
                // Don't try again.
                mUri = null;
            }
        }
        ···
        updateDrawable(d);
}

代碼不短借浊,我們只關(guān)注相關(guān)的部分÷苷校可以看到在這個方法里面

  • 通過mContext.getDrawable(mResource)獲取到了mResource解析來的Drawable對象實例蚂斤。
  • mResource就是上面方法第三步賦值我們傳入的resId來的。
  • 拿到Drawable對象d槐沼,在方法最后通過updateDrawable(d)方法更新Drawable資源曙蒸。(沒錯這個方法在上面也用到了,傳入的參數(shù)是null初始化)岗钩。

接著查看updateDrawable()方法:

    private void updateDrawable(Drawable d) {
        ···
        mDrawable = d;

        if (d != null) {
            d.setCallback(this);
            d.setLayoutDirection(getLayoutDirection());
            if (d.isStateful()) {
                d.setState(getDrawableState());
            }
            d.setVisible(getVisibility() == VISIBLE, true);
            d.setLevel(mLevel);
            mDrawableWidth = d.getIntrinsicWidth();
            mDrawableHeight = d.getIntrinsicHeight();
            applyImageTint();
            applyColorMod();

            configureBounds();
        }
}

同樣我們看重點:

  1. 將d賦值全局變量mDrawable.
  2. 通過d.getIntrinsicWidth()拿到drawable的width賦值全局變量mDrawableWidth
  3. 通過d.getIntrinsicHeight()拿到drawable的height賦值全局變來那個mDrawableHeight
  4. 調(diào)用configureBounds()方法配置邊界纽窟。

OK,去看configureBounds()方法:


    private void configureBounds() {
        ···
        int dwidth = mDrawableWidth;
        int dheight = mDrawableHeight;

        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
        
        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            mDrawable.setBounds(0, 0, dwidth, dheight);
            ···
        }
    }

代碼較多兼吓,但是并不復雜臂港,我們只看與我們討論相關(guān)的部分(中間面省略的部分是與ScaleType屬相相關(guān)的代碼,下次單獨拿出來分析)

  1. 計算出view的內(nèi)部寬高并賦值局部變量。
  2. 判斷drawable的寬高是否沒有意義视搏,或者是設置ScaleType為FIX_XY趋艘,就通過mDrawable.setBounds()方法將mDrawable的邊界范圍設置為View的內(nèi)部大小。
  3. 否則凶朗,將mDrawable的邊界范圍設置為drawable資源的寬高。(所以src默認不會拉伸圖片)

最后就是在ondraw()方法中將mDrawable繪制出來显拳。到此整個的src屬性的設置到繪制的流程已經(jīng)清楚棚愤。


接著我們來看Background屬性是如何作用于View的。

setBackgroundResource()方法入手Background屬性

在ImageView的源碼中我們通過搜索發(fā)現(xiàn)并沒有Background相關(guān)的設置方法杂数。在它的父類—View中宛畦,可以找到我們想要的代碼。

    public void setBackgroundResource(@DrawableRes int resid) {
        if (resid != 0 && resid == mBackgroundResource) {
            return;
        }
        Drawable d = null;
        if (resid != 0) {
            d = mContext.getDrawable(resid);
        }
        setBackground(d);

        mBackgroundResource = resid;
    }

代碼簡短揍移,同樣先根據(jù)resid拿到drawable對象次和,然后調(diào)用了setBackground()方法。最終調(diào)用了setBackgroundDrawable()方法那伐。在這個方法最終將drawable賦值給了一個全局變量mBackground踏施,在這個方法中并沒有發(fā)現(xiàn)設置drawable的bounds相關(guān)的方法石蔗,那么我們接著往后走,到繪制層畅形。View的onDraw()方法是空的养距,我們知道onDraw()方法其實是由View的draw()方法調(diào)用的,在draw()方法中是進行View的基本繪制日熬,onDraw()是擴展視圖自己繪制的方法棍厌。


直奔draw()方法

00-53-01.jpg

很開心,google的工程師給我們寫了很清晰的注釋竖席。第一步就是繪制background耘纱,只調(diào)用了一個方法drawBackground()。乘勝追擊毕荐。

    private void drawBackground(Canvas canvas) {
        final Drawable background = mBackground;
        if (background == null) {
            return;
        }

        setBackgroundBounds();
        ···
}

看到了束析,調(diào)用了一個setBackGroundBounds()方法,追

    void setBackgroundBounds() {
        if (mBackgroundSizeChanged && mBackground != null) {
            mBackground.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
            mBackgroundSizeChanged = false;
            rebuildOutline();
        }
    }

簡短精悍的代碼东跪,mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop)畸陡,設置了mBackground這個drawable的邊界。就是View的寬高虽填。

  • 此處是直接使用mRight - mleft 和 mBottom - mTop,也就是說忽略padding的存在丁恭。
  • 在src屬性中,使用到容器的寬高是這樣獲得的
        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
        int vheight = getHeight() - mPaddingTop - mPaddingBottom;

padding是起作用的斋日。通過兩個簡單的例子看看效果牲览。

01-09-06.jpg
01-08-32.jpg


01-10-21.jpg
01-07-19.jpg

總結(jié)

通過源碼的閱讀,清楚了ImageView 與 Background的區(qū)別與聯(lián)系以及繪制的流程:

  1. 都是Drawable資源的繪制恶守,所以他們內(nèi)容是可以互通的第献。
  2. background是View的屬性,在View層繪制(所以Background稱為背景兔港,因為它在自定義View的onDraw()方法被調(diào)用前就已經(jīng)被調(diào)用繪制了)庸毫,所有的控件都具有該屬性,src是ImageView中定義的屬性衫樊,在ImageView的onDraw方法中才被繪制飒赃。只有ImageView即其子類才有此屬性。
  3. background是縮放填充式的繪制科侈,src可以通過ScaleType設置不同的縮放效果载佳。
  4. padding屬性對background無效,但對ImageView的src是有效的臀栈。

第一次寫技術(shù)類的文章蔫慧,如有錯誤還望多多指正。歡迎分享交流权薯。
郵箱: liuj4563@163.com
原創(chuàng)博文 轉(zhuǎn)載請注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姑躲,一起剝皮案震驚了整個濱河市睡扬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肋联,老刑警劉巖威蕉,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異橄仍,居然都是意外死亡韧涨,警方通過查閱死者的電腦和手機沮焕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門眯搭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人失乾,你說我怎么就攤上這事宪哩∶浯” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵锁孟,是天一觀的道長彬祖。 經(jīng)常有香客問我,道長品抽,這世上最難降的妖魔是什么储笑? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮圆恤,結(jié)果婚禮上突倍,老公的妹妹穿的比我還像新娘。我一直安慰自己盆昙,他們只是感情好羽历,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淡喜,像睡著了一般秕磷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炼团,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天澎嚣,我揣著相機與錄音,去河邊找鬼们镜。 笑死,一個胖子當著我的面吹牛润歉,可吹牛的內(nèi)容都是我干的模狭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼踩衩,長吁一口氣:“原來是場噩夢啊……” “哼嚼鹉!你這毒婦竟也來了贩汉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤锚赤,失蹤者是張志新(化名)和其女友劉穎匹舞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體线脚,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡赐稽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了浑侥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姊舵。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖寓落,靈堂內(nèi)的尸體忽然破棺而出括丁,到底是詐尸還是另有隱情,我是刑警寧澤伶选,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布史飞,位于F島的核電站,受9級特大地震影響仰税,放射性物質(zhì)發(fā)生泄漏构资。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一肖卧、第九天 我趴在偏房一處隱蔽的房頂上張望蚯窥。 院中可真熱鬧,春花似錦塞帐、人聲如沸拦赠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荷鼠。三九已至,卻和暖如春榔幸,著一層夾襖步出監(jiān)牢的瞬間允乐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工削咆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牍疏,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓拨齐,卻偏偏與公主長得像鳞陨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瞻惋,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

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