
Drawable 是開發(fā)中經(jīng)常用到的一個概念,我們經(jīng)常用它去設(shè)置 View 的背景,背景可以一個顏色值,也可以是一張資源圖片,還可以是一個自定義的 Drawable等等。這篇文章就簡單說下 Drawable 與 View 的關(guān)系,同時結(jié)合代碼,簡要分析一下 Drawable 如何作用于 View。
Drawable 介紹
官方介紹
A Drawable is a general abstraction for "something that can be drawn." Most often you will deal with Drawable as the type of resource retrieved for drawing things to the screen; the Drawable class provides a generic API for dealing with an underlying visual resource that may take a variety of forms. Unlike a
View, a Drawable does not have any facility to receive events or otherwise interact with the user.
簡單翻譯下:
Drawable 是 “所有可繪制東西” 的一個抽象,大多數(shù)時候,我們只需要把各種不同類型的資源作為轉(zhuǎn)化為 drawable,然后 View 會幫我們把它渲染到屏幕上。Drawable 類提供了一個通用 API,用于解析轉(zhuǎn)化各種可視資源到 Canvas,跟 View 不一樣,Drawable 不能接受任何事件以及用戶交互。
總而言之,Drawable 就是一個可繪制東西的抽象,相比 View,它更純粹,就是用來做繪制相關(guān)事情的,它處理不了用戶交互事件,也不需要處理,所有交互相關(guān)的事都是由 View 來完成,但是背景相關(guān)的事大都可以通過 Drawable 來完成。
一般的,我們要為 View 設(shè)置背景,可通過如下幾種方式:
- 通過顏色為 View 設(shè)置背景
- 通過自定義的 shape 設(shè)置背景
用顏色設(shè)置背景
通過 View 的 setBackgroundColor 方法可以設(shè)置顏色為 View 的背景。
button.setBackgroundColor(Color.YELLOW);
效果如下:

用自定義的 shape 設(shè)置背景
先用 xml 自定義一個圓角空心描邊矩形 shape
<shape
android:shape="rectangle">
<corners android:radius="4dp"/>
<solid android:color="#fff"/>
<stroke android:color="#ef6f06" android:width="1dp"/>
</shape>
通過代碼進(jìn)行設(shè)置
button.setBackgroundResource(R.drawable.bk_normal);
效果如下:

可以看到,給 View 設(shè)置背景 drawable 非常簡單,具體通過如下的 API 實現(xiàn)背景設(shè)置:
- setBackgroundColor(@ColorInt int color)
- setBackgroundResource(@DrawableRes int resid)
- setBackground(Drawable background)
但是設(shè)置的背景 Drawable 是如何在 View 上生效的,可能很多人沒去思考過這個問題,這里簡單分析下。
Drawable 如何作用于 View
Drawable 是一個抽象類,這里通過它的的幾個抽象方法就能大概猜得出 Drawable 如何作用于 View,下面是 Drawable 的幾個抽象方法:
public abstract void draw(@NonNull Canvas canvas);
public abstract void setAlpha(@IntRange(from=0,to=255) int alpha);
public abstract void setColorFilter(@Nullable ColorFilter colorFilter);
public abstract @PixelFormat.Opacity int getOpacity();
可以看到這里有一個 draw 方法,并且參數(shù)中提供了 canvas 對象。
public abstract void draw(@NonNull Canvas canvas);
現(xiàn)在可以想象一下,View 通過 setBackground 方法為自己設(shè)置了一個 drawable 對象后,而 drawable 又有一個 draw 方法,那么 View 繪制自己的背景時,直接調(diào)用 drawable 對象的的 draw 方法,這個 draw 方法需要一個 canvas 對象,這里可直接把 View 的 Canvas 對象傳遞過去,那么 Drawable 就可以成功的把自己的繪制內(nèi)容應(yīng)用到 View 之上。
這個過程,相當(dāng)于 View 把自己的背景繪制功能外包給了 Drawable 對象。
而且,這也是一種非常好的設(shè)計模式,View 負(fù)責(zé)測量自己大小,給自己指定位置,并繪制 View 前景 ,但是把自己的背景繪制外派給了更獨立的 drawable 去做,從而做到了讓自己更加輕量,現(xiàn)在 View 就成功的把背景繪制職責(zé)分配給了自己的 drawable 對象。
盡管上面只是想象,但事實上也確實如此。通過查看源碼,在 View 中有一個私有方法 drawBackground,它的作用就是把 drawable 繪制在 canvas 上。
/**
* Draws the background onto the specified canvas.
* @param canvas Canvas on which to draw the background
*/
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
setBackgroundBounds();
//省略部分代碼
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
//調(diào)用 drawable 自己的 draw 方法,從而將繪制的功能移交到 drawable 類
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
Drawable 與 View 的關(guān)系
- View 是皮,它是一個具體的東西,看得見摸得著,因為它自己可以測量自己打消、指定自己位置,還能接受 onTouch 事件從而處理用戶交互。
- Drawable 是毛,它可以不存在,因為 View 完全可以在自己的 onDraw 時機(jī)中,自己把自己繪制了,無需把繪制進(jìn)行外包。
- 但是 Drawable 更專業(yè),更獨立,它提供了一整套豐富的背景 Drawable 機(jī)制,它有豐富的實現(xiàn)類,可以提供給 View 進(jìn)行方便的背景設(shè)置,對 View 來說 drawable 提供的那些實現(xiàn)類開箱即用,還可以減少自己的職能,節(jié)省自己的維護(hù)開銷,何樂而不為。
總結(jié)
Drawable 是一個抽象的概念,只要理解了它跟 View 的關(guān)系,其實 Drawable 的想象力會非常大。通過自定義 Drawable,可以在 Drawable 中完成各種繪制邏輯,自定義完成后,只需要讓 View 調(diào)用 setBackground() 方法,把自定義的 Drawable 傳遞進(jìn)去,這樣就可以方便把自定義 Drawable 和 View 關(guān)聯(lián)在一起。
之前寫過一個轉(zhuǎn)菊花的 Loading 效果,就是用自定義 Drawable 實現(xiàn)的,目前已開源在 github,感興趣的去看看。
FlowerLoading: Android loading or progress view, just like iOS IndicatorView.