1准脂、Drawable 簡介
Drawable——可簡單理解為可繪制物邪媳,表示一些可以繪制在 Canvas 上的對象捐顷。在日常的工作開發(fā)中,我們?yōu)?UI 配置背景雨效、圖片迅涮、動畫等等界面效果的時候,需要和眾多的 Drawable 打交道徽龟。每種 Drawable 的適用范圍不同叮姑,我們有必要了解每種 Drawable 的特點以及使用方式,才能在工作中得心應(yīng)手顿肺,少走彎路戏溺。
具體的配置、使用方法以及最終的界面效果大家可以在本文的附件里面看到屠尊。Drawable 在 Android 中的繼承關(guān)系如下旷祸,其中,紅框標(biāo)注的幾種 Drawable 是我們在開發(fā)中比較常用的一些:
Drawable 中比較重要的方法有以下幾種:
Drawable
|- createFromPath
|- createFromResourceStream
|- createFromStream
|- createFromXml
|
|- inflate : 從XML中解析屬性讼昆,子類需重寫
|- setAlpha : 設(shè)置繪制時的透明度
|- setBounds : 設(shè)置Canvas為Drawable提供的繪制區(qū)域
|- setLevel : 控制Drawable的Level值托享,這個值在ClipDrawable、RotateDrawable浸赫、ScaleDrawable闰围、AnimationDrawable等Drawable中有重要作用;區(qū)間為[0, 10000]
|- draw(Canvas) : 繪制到Canvas上既峡,子類必須重寫
其中羡榴,比較重要的方法是inflate
和draw
。inflate 方法用于從 XML 中讀取 Drawable 的配置运敢,draw 方法則實現(xiàn)了把一個 Drawable 確切的繪制到一個 Canvas 上面——draw 方法為一個abstract
抽象方法校仑,子類必須進行重寫。inflate 方法在Drawable.createFromXmlInner
中被調(diào)用:
我們可以看出传惠,在從 XML 中創(chuàng)建一個 Drawable 時迄沫,步驟如下:
- 先根據(jù) XML 節(jié)點名稱來決定創(chuàng)造什么類型的 Drawable;然后 new 出相應(yīng)的 Drawable;
- 再為該 Drawable 調(diào)用 inflate 方法,讓其把配置加載起來——因為每種 Drawable 會重寫 inflate 方法卦方,所以羊瘩,可以正確加載到各項配置及屬性。XML 的配置我們稍后再講盼砍。
setAlpha
方法用于設(shè)置一個 Drawable 的透明度尘吗,setBounds
用來指定當(dāng)執(zhí)行繪制時,在 Canvas 上的位置和區(qū)域浇坐。比如我們自定義一個 View摇予,在其onDraw
中繪制一個BitmapDrawable
,我們設(shè)置了 BitmapDrawable 的 Alpha 和 Bounds吗跋,代碼如下:
Drawable baseDrawable = getResources().getDrawable(R.drawable.base);
baseDrawable.setAlpha(100);
baseDrawable.setBounds(10, 20, 500, 300);
imageContent.setDrawable(baseDrawable);
繪制后的表現(xiàn)如下:
上圖中侧戴,第一個區(qū)域是正常繪制的,第二個我們?yōu)?Drawable 設(shè)置了Alpha和Bounds跌宛,可以看出酗宋,右邊深藍色的純色部分為整個 Canvas 的大小,設(shè)置了 100 的 Alpha 透明度后疆拘,圖片把后面深藍色的顏色也給透過來了蜕猫,并且 Bounds 決定了 Canvas 上繪制該 Drawable 的區(qū)域大小和位置。
2哎迄、ColorDrawable
接下來我們逐一介紹 Drawable回右,著重介紹幾種常用的 Drawable隆圆。由于在開發(fā)中這些 Drawable 大多在 XML 中進行配置,所以我們結(jié)合 XML 的配置類介紹翔烁。先從ColorDrawable
開始渺氧,這個應(yīng)該是最簡單的一種 Drawable 了,它用一個顏色值來表示:
color
|- color="#xxxxxx | @color/color_value"
|
比如我們的一個 ColorDrawable 的 XML 配置如下蹬屹,以<color>
作為根節(jié)點:
<color
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000ff"/>
使用的時候和其他 Drawable 的使用方法類似侣背,可以通過Resource.getDrawable
來獲取,或者在 XML 里面配置:
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/blue_drawable"/>
這個 View 的界面表現(xiàn)如你所想慨默,是一坨藍色:
Java代碼實現(xiàn):
Resources res = getResources();
ColorDrawable colorDrawable = new ColorDrawable();
colorDrawable.setColor(res.getColor(R.color.skin_black_item));
3贩耐、BitmapDrawable
BitmapDrawable
以<bitmap>
作為根節(jié)點:
bitmap
|- src="@drawable/res_id"
|- antialias="[true | false]"
|- dither="[true | false]"
|- filter="[true | false]"
|- tileMode="[disabled | clamp | repeat | mirror]"
|- gravity="[top | bottom | left | right | center_vertical |
| fill_vertical | center_horizontal | fill_horizontal |
| center | fill | clip_vertical | clip_horizontal]"
|
這個比較復(fù)雜一點了,我們逐一介紹各個屬性:
- src:表示該 BitmapDrawable 引用的位圖厦取,該圖片為 png潮太、jpg 或者 gif;
-
antialias:表示是否開啟
抗鋸齒
虾攻; -
dither:表示當(dāng)位圖和屏幕的像素配置不同時消别,是否允許
抖動
。比如一張位圖的像素為 ARGB_8888 32 位色台谢,而屏幕像素為 RGB_565寻狂; - filter:是否允許為位圖進行濾波以獲取平滑的縮放效果;
-
gravity:定義位圖的
gravity
朋沮,當(dāng)位圖小于容器時蛇券,該屬性指定了位圖在容器中的停靠位置
和繪制方式
樊拓。 -
tileMode:表示當(dāng)位圖小于容器時纠亚,執(zhí)行
“平鋪”
模式,并且指定鋪磚的方法筋夏。該屬性覆蓋 gravity 屬性——當(dāng)指定了該屬性后蒂胞,gravity 屬性即使設(shè)置了,也將不起作用条篷。
其中骗随,gravity
和tileMode
這兩個屬性比較有意思,我們著重來進行介紹赴叹。gravity 的默認(rèn)值為fill
——亦即在水平和垂直方向均進行縮放鸿染,使得圖片可以填充到整個 View 里面。
比如我們有一張如下的圖片:
為了比較好的展現(xiàn)clamp 鉗位模式
乞巧,注意這張圖涨椒,我們在右邊緣和下邊緣用了黑白交替的邊線。我們的 XML 配置極其簡單,以<bitmap>
作為根節(jié)點:
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/car"
android:tileMode="repeat"/>
當(dāng)這個 BitmapDrawable 放入一個比它大的容器中時蚕冬,tileMode 就起作用了:
-
repeat
模式:將重復(fù)貼該圖免猾,直到填充完容器:
-
clamp
模式:鉗位模式,將沿用下邊囤热、右邊邊緣的像素值分水平猎提、垂直兩個方向擴展填充剩余位置:
-
mirror
模式:鏡像模式,將按水平赢乓、垂直鏡像重復(fù)來填充剩余位置:
-
disabled
:禁用任何填充方法,將使用整個位圖進行縮放填充石窑。
我們接著來看 gravity 屬性牌芋,該屬性也比較容易理解:
-
top
:在頂部水平中心繪制;其他類如 left松逊、right躺屁、bottom 和 top 類似;
當(dāng)然经宏,我們可以使用“|”來組合犀暑,達到特殊的效果,比如當(dāng) gravity 為bottom|right
時烁兰,表現(xiàn)如下:
center_horizontal
耐亏、center_vertical
將在水平爷辙、垂直兩個方向上居中洗出。當(dāng)單獨使用 top/left/right/bottom 四個值時,默認(rèn)帶了這兩個中的值:比如 top == top|center_horizontal鄙才;fill_horizontal
主之、fill_vertical
將在水平择吊、垂直兩個方向上進行縮放填充,默認(rèn)也是帶了center_horizontal
或者center_vertical
這兩個值的槽奕;
下面是“fill_vertical”
的表現(xiàn):
下面是“fill_vertical|left”
的表現(xiàn):
-
clip_horizontal
几睛、clip_vertical
將在 Drawable 比容器大時,按水平粤攒、垂直方向進行裁剪所森,下面是 gravity 為“clip_vertical”
的情況,可以看出夯接,裁剪了小汽車的首尾:
在實際的開發(fā)中必峰,我們要活用這些 gravity 的值,可以通過“|”
來獲取各種想要的效果钻蹬。
Java代碼實現(xiàn):
Resources res = getResources();
Bitmap bmp = BitmapFactory.decodeResource(res, R.drawable.adt_48);
BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bmp);
bitmapDrawable.setTileModeX(TileMode.MIRROR);
bitmapDrawable.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
4吼蚁、NinePatchDrawable
4.1、.9.png圖片資源
這是一種比較高端的 Drawable。其實就是九宮貼圖——這種 Drawable 契合了 Android 中的“.9.png”
文件肝匆。這種圖片資源的特點在于:
- 在一張普通的 png 圖片四周粒蜈,分別向外擴展了一個像素;
- 用這些擴展的像素旗国,可以描邊枯怖,描邊用來規(guī)定
可縮放區(qū)域
和內(nèi)容padding區(qū)域
;
4.1.1能曾、.9.png的擴展區(qū)域
比如我們現(xiàn)在有一張 .9.png 圖片如下:
我們在四周看到了一像素的黑點度硝,這些黑點分別在四周圍成四個邊線
。四個圓角處都是透明的寿冕。那么蕊程,左、上兩條邊規(guī)定了當(dāng)按鈕被縮放時的可縮放區(qū)域
驼唱。比如下面紅色邊框圈出的矩形內(nèi)的區(qū)域藻茂,就是可縮放區(qū)域,這個區(qū)域外的區(qū)域玫恳,在執(zhí)行縮放時均保留原來的像素比例辨赐。
比如一個按鈕各個角度拉伸,都可以保留圓角的圓潤京办,而不會發(fā)生鋸齒或者糊掉掀序。我們的布局文件如下:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="100dp"
android:layout_height="40dp"
android:background="@drawable/btn_normal"/>
<Button
android:layout_width="150dp"
android:layout_height="80dp"
android:layout_marginLeft="10dp"
android:background="@drawable/btn_normal"/>
</LinearLayout>
當(dāng) btn_normal 是一個 .9.png 文件時,界面表現(xiàn)如下:
但如果我們的 background 對應(yīng)的圖片如果是一張非 .9.png 的原圖惭婿,那么森枪,界面表現(xiàn)有些糟,明顯看出审孽,圓角部分糊掉了县袱,并且圓角看起來很詭異:
4.1.2、.9.png 的 padding 區(qū)域
這就是 .9.png 的妙處佑力。這種格式圖片的右式散、下兩個邊緣的像素點,規(guī)定了padding區(qū)域
打颤,也就是說暴拄,內(nèi)容的繪制時的 padding:
上面的紅色邊框圈出的矩形區(qū)域規(guī)定了內(nèi)容繪制的區(qū)域。比如我們把上面的圖片用作一個 Button 的 background 時编饺,整個按鈕的文本將明顯偏上:
通過padding區(qū)域的控制乖篷,我們可以輕松實現(xiàn)一個按鈕按下后文字也相應(yīng)下移幾個像素的點擊效果。
4.1.3透且、.9.png 的制作
制作簡單提一下撕蔼,在 AndroidSDK 的安裝目錄中豁鲤,tools 文件夾下有一個“draw9patch.bat”
文件,啟動該 bat鲸沮,就相應(yīng)打開了 .9.png 的制作工具:
我們把一個簡單的png拖入這個窗口琳骡,就可以編輯了。用鼠標(biāo)左鍵在邊緣點擊以點出像素點讼溺,用鼠標(biāo)右鍵刪除像素點楣号。在右邊可以實時預(yù)覽繪制效果:
4.2、NinePatchDrawable 的 XML 配置
這種 Drawable 的 XML 節(jié)點表述如下怒坯,以<nine-patch>
作為根節(jié)點:
nine-patch
|- src="@drawable/9_png_resid"
|- dither="[true | false]"
|
其中炫狱,dither 屬性,和之前 BitmapDrawable 中將的一樣剔猿,就是像素配置不同時视译,是否允許抖動。src 比較重要艳馒,這個值指向的必須是一個“.9.png”格式的圖片憎亚,否則员寇,底層NinePatchDrawable.inflate
方法在解析的時候弄慰,會拋出一個XmlPullParserException
異常:
我們可以看出,上圖中bitmap.getNinePatchChunk
這個方法蝶锋,獲取 9 宮的各項信息陆爽,如果從一個 Bitmap 對象中得不到這些信息,則表示這個圖片非“.9.png”格式的圖片扳缕,就拋出異常慌闭。
其實,“.9.png”的圖片躯舔,本質(zhì)上是一張普通的 png 圖片驴剔。比如,我們有一張名為“btn_normal.9.png”的圖片粥庄,可以在代碼中這樣使用:
View imageContent = findViewById(R.id.xxx);
Resources res = getResources();
NinePatchDrawable normal = (NinePatchDrawable) res.getDrawable(R.drawable.btn_normal);
imageContent.setBackground(normal);
4.3丧失、NinePatchDrawable 與 .9.png 圖片的映射
那么,Android 是怎樣把這張圖片映射為一個 NinePatchDrawable 的呢惜互?原來布讹,這張圖片開始被當(dāng)作普通的 Bitmap,從 Resources.getDrawable 方法中可以看出端倪:
在 getDrawable 中調(diào)用了loadDrawable
训堆,在 loadDrawable 方法中有一個緩存策略描验,我們先不管,直接看加載資源的部分:
可以看出坑鱼,對 XML 配置類型的 Drawable膘流,使用loadXmlResourceParse
加載,然后使用Drawable.createFromXml
這個靜態(tài)方法進行創(chuàng)建,得到 Drawable 對象睡扬。對于其他類型的 Drawable盟蚣,先使用openNonAsset
得到一個流對象,然后使用Drawable.createFromResourceStream
這個靜態(tài)方法進行創(chuàng)建卖怜。Drawable.createFromXml 這個方法最終會調(diào)用Drawable.createFromXmlInner
屎开,這個方法我們前面 Drawable 簡介里面已經(jīng)介紹過了。我們著重看 Drawable.createFromResourceStream 這個方法:
在這個方法中马靠,我們先從流中解析得到一個 Bitmap 對象——這個對象本質(zhì)上和其他所有類型的圖片資源沒任何區(qū)別奄抽。區(qū)別在于接下來調(diào)用的Bitmap.getNinePatchChunk
、NinePatch.isNinePatchChunk
這兩個方法甩鳄,通過這兩個方法的結(jié)合調(diào)用逞度,可以判斷這個 Bitmap 是否是一個合格的“.9.png”圖片。接下來進入drawableFromBitmap
:
最后妙啃,根據(jù)九宮信息 np 這個參數(shù)是否為 null档泽,來決定創(chuàng)建什么對象∫靖埃可以看出馆匿,對“.9.png”格式的圖片,最終會創(chuàng)建一個 NinePatchDrawable 對象燥滑,對于其他普通的 png渐北、jpg 等圖片,創(chuàng)建相應(yīng)的 BitmapDrawable 對象铭拧。一切一目了然赃蛛。
Java代碼實現(xiàn):
這部分我們使用draw9patch工具很容易制作,一般不會在代碼中進行創(chuàng)建NinePatchDrawable對象搀菩,也不推薦在代碼中這樣做呕臂。
5、StateListDrawable
這個 Drawable 類型幾乎是我們開發(fā)中最常用的類型了肪跋,為什么呢歧蒋?因為它是根據(jù)一系列的狀態(tài)來控制繪制表現(xiàn)的,這一系列狀態(tài)契合了我們界面控件的各個狀態(tài)澎嚣。界面控件的狀態(tài)一般有:獲取焦點疏尿、失去焦點、普通狀態(tài)易桃、按下狀態(tài)褥琐、可點擊狀態(tài)、不可點擊狀態(tài)晤郑、選中狀態(tài)敌呈、未選中狀態(tài)贸宏、勾選狀態(tài)、未被勾選狀態(tài)磕洪、激活狀態(tài)吭练、未被激活狀態(tài)等等。
StateListDrawable
以<selector>
作為根節(jié)點:
selector
|- item
| |- drawable="@drawable/drawable_id"
| |- state_pressed="[true | false]"
| |- state_focused="[true | false]"
| |- state_selected="[true | false]"
| |- state_hovered="[true | false]"
| |- state_checked="[true | false]"
| |- state_checkable="[true | false]"
| |- state_enabled="[true | false]"
| |- state_activated="[true | false]"
| |- state_window_focused="[true | false]"
|
一個selector
以多個item
來組成析显,每個 item 由 0 個或者多個狀態(tài)和一個 drawable 來表示鲫咽,當(dāng)控件的狀態(tài)變化后,將根據(jù)控件當(dāng)前的狀態(tài)谷异,來進行匹配分尸,匹配一個最適合當(dāng)前狀態(tài)的 item,然后用這個 item 的 drawable 來進行繪制歹嘹。
比如箩绍,我們一個普通按鈕的 selector 如下:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@drawable/pressed_btn" />
<item android:drawable="@drawable/normal_btn" />
</selector>
我們定義了一個按鈕的普通狀態(tài)和按下狀態(tài)的 Drawable,使用方法如下:
<Button
android:layout_width="200dp"
android:layout_height="60dp"
android:textColor="#e22"
android:background="@drawable/flat_button_drawable"
android:text="Flat Button" />
那么尺上,在普通狀態(tài)和按下狀態(tài)中材蛛,界面表現(xiàn)分別如下:
普通狀態(tài)????
按下狀態(tài)????
Cool!除了按下狀態(tài)的紅色有點刺眼外怎抛,看起來還不錯卑吭,是吧。其實抽诉,我們可以通過控件狀態(tài)陨簇,來控制普通態(tài)吐绵、按下態(tài)的按鈕文字顏色迹淌。我們新建一個 XML,放入res/color
文件夾下己单,比如起名為 btn_text_color.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#fff"/>
<item android:color="#e22"/>
</selector>
我們在Button中的配置如下唉窃,通過設(shè)置android:textColor來控制按鈕的文本顏色:
<Button
android:layout_width="200dp"
android:layout_height="60dp"
android:textColor="@color/btn_text_color"
android:background="@drawable/flat_button_drawable"
android:text="Flat Button" />
現(xiàn)在,一個高大上的扁平化的按鈕效果出爐了:
普通狀態(tài)????
按下狀態(tài)????
在實際操作中纹笼,我們可能要為多種狀態(tài)來進行設(shè)置纹份,可以靈活運用:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:state_pressed="true"
android:state_selected="false"
android:drawable="@drawable/pressed_btn"/>
<item android:drawable="@drawable/normal_btn"/>
</selector>
這樣,第一個item將只匹配未被禁用且當(dāng)前為按下狀態(tài)且未被選中狀態(tài)廷痘。其他狀態(tài)均使用第二個item蔓涧。
當(dāng)然,不踩幾個坑笋额,怎么能做一名合格的開發(fā)者呢元暴?
注意:
如果有不帶任何狀態(tài)的 item 的話,這個item一定要放在整個 item 列表的最下面兄猩。否則茉盏,所有的狀態(tài)均可優(yōu)先匹配到這個 item鉴未,其他 item 將得不到匹配。因為匹配的時候是一個遍歷操作鸠姨,如果遍歷找到和當(dāng)前狀態(tài)符合的 Drawable铜秆,就直接返回。
Java代碼實現(xiàn):
Resources res = getResources();
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(
new int[] {android.R.attr.state_pressed},
res.getDrawable(R.drawable.blue_drawable));
stateListDrawable.addState(
new int[] {
android.R.attr.state_pressed,
android.R.attr.state_enabled},
res.getDrawable(R.drawable.bmp_drawable));
stateListDrawable.addState(
new int[] {},
res.getDrawable(R.drawable.bkgnd_normal));
6讶迁、ClipDrawable
ClipDrawable
允許我們對一個 Drawable 進行剪裁操作连茧,在繪制的時候只繪制剪裁的部分。這里最關(guān)鍵的是Drawable.setLevel
方法在起作用巍糯,在為一些控件比如進度條梅屉、音量控制條等設(shè)置 UI 效果的時候,一般會使用 ClipDrawable鳞贷,否則坯汤,你的進度在界面上將得不到刷新。
ClipDrawable以<clip>
作為根節(jié)點:
clip
|- drawable="@drawable/drawable_id"
|- clipOrientation="[horizontal | vertical]"
|- gravity="[ ... ]"
|
clipOrientation
決定了裁剪的方向搀愧,默認(rèn)為horizontal
——表示水平方向剪裁惰聂;而 gravity 的取值和之前介紹的類似,結(jié)合 clipOrientation 決定了剪裁發(fā)生的位置——默認(rèn)為left咱筛,就是當(dāng) clipOrientation 為 horizontal 時搓幌,剪裁發(fā)生在 drawable 的右側(cè)。
最主要的繪制我們來看ClipDrawable.draw方法:
根據(jù) AndroidSDK 的規(guī)范迅箩,setLevel
的 level 值在[0, 10000]
這個區(qū)間內(nèi)溉愁。可以看出饲趋,在繪制的時候拐揭,根據(jù) level 值和 gravity 算出要剪裁的區(qū)域,然后在 Canvas 上執(zhí)行 clipRect奕塑,從而達到剪裁效果堂污。
XML的配置也很簡單:
<clip
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/normal_btn"
android:clipOrientation="vertical"
android:gravity="top" />
Java代碼實現(xiàn):
Resources res = getResources();
NinePatchDrawable btnNormal = (NinePatchDrawable) res.getDrawable(R.drawable.btn_normal);
ClipDrawable clipDrawable = new ClipDrawable(
btnNormal, Gravity.TOP, ClipDrawable.VERTICAL);
clipDrawable.setLevel(500);
我們后續(xù)結(jié)合LayerDrawable
來看ClipDrawable
在進度條等 UI 上的配置方式。
7龄砰、LayerDrawable
LayerDrawable
可以將一組 Drawable 按 XML 中定義的順序?qū)盈B起來進行繪制盟猖,并可以設(shè)定每層 Drawable 的 id、位置等等换棚。ProgressBar
這個控件的背景切圖式镐,可以通過 LayerDrawable 來進行配置。LayerDrawable 以<layer-list>
作為根節(jié)點:
layer-list
|- item
| |- drawable="@drawable/drawable_id"
| |- id="@+id/xxx_id"
| |- top="dimension"
| |- left="dimension"
| |- right="dimension"
| |- bottom="dimension"
|
每組 Drawable 由<item>
節(jié)點進行配置固蚤,item 中 drawable 表示了這層 Drawale 引用的繪圖資源 ID娘汞,id
屬性表示了這層 Drawable 的ID,top颇蜡、left价说、right辆亏、bottom
這四個屬性發(fā)布表示與各個方向的間距。比如一個簡單的 LayerDrawable 如下:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/red_color"
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp"/>
<item
android:drawable="@drawable/green_color"
android:bottom="20dp"
android:left="20dp"
android:right="20dp"
android:top="20dp"/>
<item
android:drawable="@drawable/blue_color"
android:bottom="30dp"
android:left="30dp"
android:right="30dp"
android:top="30dp"/>
</layer-list>
那么鳖目,繪制出來的效果如下(其中扮叨,灰色那一層是Activity的背景):
接下來我們看看LayerDrawable在ProgressBar中的配置:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@android:id/background"
android:drawable="@drawable/file_background"/>
<item android:id="@android:id/progress">
<clip android:drawable="@drawable/file_progress"/>
</item>
<item android:id="@android:id/secondaryProgress">
<clip android:drawable="@drawable/file_cache_progress"/>
</item>
</layer-list>
可以看出,我們在配置的時候领迈,分別為每個 item 指定了 id——這些 id 對應(yīng)表示了 ProgressBar 中每種進度狀態(tài):background
對應(yīng)整個 ProgressBar 的背景彻磁,progress
對應(yīng)當(dāng)前的進度背景,而secondaryProgress
對應(yīng) secondaryProgress 的進度背景(一般我們用來做緩沖進度——和優(yōu)酷視頻的緩沖類似)狸捅。另外我們看出衷蜓,結(jié)合使用了 ClipDrawable——因為 ProgressBar 的實現(xiàn)中,正是結(jié)合Drawable.setLevel
來進行刷新進度的尘喝,在前面講過磁浇,ClipDrawable 恰好在onDraw
繪制中,對 Level 做了相應(yīng)的處理:
這里有一個方法:LayerDrawable.findDrawableByLayerId
朽褪,這個方法可以獲取 id 對應(yīng)的 Drawable置吓。
Java代碼實現(xiàn):
Resources res = getResources();
LayerDrawable layerDrawable = new LayerDrawable(
new Drawable[] {
res.getDrawable(R.drawable.red_color),
res.getDrawable(R.drawable.green_color),
res.getDrawable(R.drawable.blue_color)
});
layerDrawable.setId(0, R.id.action_settings);
layerDrawable.setId(1, R.id.switchBtn);
layerDrawable.setLayerInset(0, 10, 10, 10, 10);
layerDrawable.setLayerInset(1, 20, 20, 20, 20);
8、AnimationDrawable
8.1缔赠、AnimationDrawable 的使用
借助AnimationDrawable
衍锚,我們可以輕松實現(xiàn)基于一系列 Drawable 幀的動畫效果。AnimationDrawable 提供了一系列簡單易用的接口來幫助我們:
AnimationDrawable
|- setOneShot : 設(shè)置動畫是否單次播放嗤堰,默認(rèn)為false戴质,表示不循環(huán)
|- start : 開始播放動畫,如果已經(jīng)在播放中踢匣,則不起作用
|- end : 結(jié)束播放
|
一般我們在 XML 里面進行配置動畫告匠,代碼中手工寫的方式不推薦。AnimationDrawable 以<animation-list>
作為根節(jié)點:
animation-list
|- oneshot="[true | false]"
|- visible="[true | false]"
|- item
| |- drawable="@drawable/drawable_id"
| |- duration="xms"
|
animation-list 節(jié)點內(nèi)的oneshot
屬性表示該動畫是否只播放一次符糊,當(dāng)這個值為 false 的時候凫海,表示循環(huán)播放——這是默認(rèn)值呛凶。其他的一系列動畫效果男娄,均由一組<item>
節(jié)點來進行配置,item 中的duration
表示這一幀和上一幀的時間間距漾稀,以 ms 為單位模闲。比如我們有一個簡單的動畫配置如下:
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/red_color"
android:duration="500"/>
<item
android:drawable="@drawable/green_color"
android:duration="500"/>
<item
android:drawable="@drawable/blue_color"
android:duration="500"/>
</animation-list>
我們可以將該動畫效果施加到一個 View 上:
View imageContent = findViewById(R.id.xxx);
AnimationDrawable drawable = (AnimationDrawable) res.getDrawable(R.drawable.animation_drawable);
imageContent.setBackground(drawable);
drawable.start();
這樣,我們在這個 View 上可以看到每隔 500ms 便變換一次顏色的動畫效果崭捍。當(dāng)然尸折,這只是一個 demo,利用 AnimationDrawable殷蛇,我們可以做出更酷的動畫实夹。
我們一般在 XML 里面配置 AnimationDrawable橄浓,通過 Resources.getDrawable
方法來獲取它。雖然我們不推薦在代碼里面手工創(chuàng)建 AnimationDrawable亮航,但萬一哪天你需要它呢荸实?
Java代碼實現(xiàn):
Resources res = getResources();
AnimationDrawable animationDrawable = new AnimationDrawable();
animationDrawable.addFrame(res.getDrawable(R.drawable.red_color), 500);
animationDrawable.addFrame(res.getDrawable(R.drawable.green_color), 500);
animationDrawable.addFrame(res.getDrawable(R.drawable.blue_color), 500);
animationDrawable.setOneShot(false);
imageContent.setBackground(animationDrawable);
animationDrawable.start();
8.2、AnimationDrawable 的原理
我們只是把一個 AnimationDrawable 塞入了一個 View 的 background 中缴淋,那么這些動畫的變換准给,是怎么響應(yīng)到 View 上的呢?原來重抖,這一切都是Drawable.Callback
這個回調(diào)在起作用:
我們通過 Drawable.setCallback
來設(shè)置一個Callback
露氮,這個 Callback 中有三個方法:
- invalidateDrawable:重繪 Drawable;
-
scheduleDrawable:在 when 規(guī)定的 ms 后钟沛,執(zhí)行 what 這個
Runnable
畔规;(這里可以看出動畫的端倪了) - unscheduleDrawable:異步執(zhí)行這個 what;用來結(jié)束動畫等恨统。
View 類實現(xiàn)了 Drawable.Callback 這個接口
油讯,在我們調(diào)用View.setBackground
方法為 View 設(shè)置背景的時候,會把 View 的 this 塞入 Drawable 中作為 Callback:
而在 AnimationDrawable 自己實現(xiàn)了Runnable
這個接口延欠,在run
方法中陌兑,通過調(diào)用AnimationDrawable.nextFrame
方法,提供了動畫幀的切換由捎、終止判斷等操作兔综。
在這里首先使用selectDrawable
把對應(yīng)幀的 Drawable 選為激活的,然后在scheduleSelf
中狞玛,通過調(diào)用Drawable.Callback.scheduleDrawable
這個 Callback 方法软驰,可以達到動畫幀按時間間隔切換的效果。
9心肪、其他 Drawable 及總結(jié)
基本上我們在工作中最常用的幾類 Drawable 如上所示锭亏。其他的一些 Drawable 有時也會用到,也很有趣硬鞍。比如ShapeDrawable
慧瘤、RotateDrawable
、ScaleDrawable
以及InsetDrawable
固该。這些 Drawable 可以在工作中確實需要用到的時候去參考 SDK 進行學(xué)習(xí)和靈活運用锅减,在這里簡單介紹下這幾種 Drawable 的作用和使用方法,以及一些效果截圖伐坏。
9.1怔匣、ShapeDrawable
通過在 XML 中配置 ShapeDrawable,我們可以輕松繪制矩形桦沉、線段每瞒、圓角矩形金闽、漸變等圖形作為 background 而不需要切圖。ShapeDrawable 以<shape>
作為根節(jié)點剿骨;需要熟悉子節(jié)點的有:corners
呐矾、gradient
、padding
懦砂、size
蜒犯、solid
、stroke
等荞膘;比如下面是一個簡單的配置及效果展現(xiàn):
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="5dp"/>
<gradient
android:angle="90"
android:endColor="#ddd"
android:startColor="#343434"
android:type="linear"/>
<stroke
android:width="2dp"
android:color="#00f"/>
</shape>
我們通過android:shape
指定這個 shape 是一個矩形(rectangle
)罚随,用子節(jié)點corners
為矩形加上圓角,使之變成一個圓角矩形羽资;再使用gradient
子節(jié)點來施加一個漸變效果淘菩,漸變的類型用android:type
指定為線性漸變(linear);最后再使用stroke
子節(jié)點為整個圖形加上一個 2dp 寬的藍色外邊框屠升。其效果圖如下:
9.2潮改、RotateDrawable
RotateDrawable
可以結(jié)合當(dāng)前 Drawable 的 level 值,進行旋轉(zhuǎn)腹暖。level 值每增加一汇在,其旋轉(zhuǎn)角度旋轉(zhuǎn)(toDegrees – fromDegrees) / 10000
。比如下圖是一張正常的圖片:
我們通過一個 XML 進行旋轉(zhuǎn)脏答,其中android:fromDegrees
和android:toDegrees
確定了旋轉(zhuǎn)的起始角度和終止角度糕殉,android:pivotX
、android:pivotY
確定了旋轉(zhuǎn)中心點的位置:
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/android_robot"
android:fromDegrees="0"
android:toDegrees="180"
android:pivotX="50%"
android:pivotY="50%"/>
我們在 UI 線程中控制這個 RotateDrawable 的 Level 值殖告,可以獲得一個旋轉(zhuǎn)的動畫效果:
我們截取了部分動畫效果的過程阿蝶,如下:
9.3、ScaleDrawable
ScaleDrawable
可以結(jié)合當(dāng)前 Drawable 的 level 值黄绩,進行圖片的縮放羡洁,同樣結(jié)合Handler
和Timer
,我們可以得到一個簡單的縮放動畫爽丹。
9.4筑煮、InsetDrawable
InsetDrawable
可以把一個 drawable 資源嵌入到其他的資源內(nèi)部,并且在四周可以留下邊距习劫。比如我們有時候需要一個左右各留白 15dp 的ListView
的分隔線咆瘟,我們可以用 InsetDrawable 來做。為什么不使用切圖的方式來留白呢——注意诽里,我們這里要求是 15dp,而不是 15pixel飞蛹,如果切圖的話谤狡,只能用像素單位留白灸眼,但這導(dǎo)致在不同的設(shè)備上可能用戶看到的留白的間距不統(tǒng)一。
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/seperator_line"
android:insetLeft="15dp"
android:insetRight="15dp"/>
我們應(yīng)用到一個 ListView 的分隔符上:
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/inset_drawable"
android:dividerHeight="1dp"/>
這樣墓懂,我們得到了一個首尾均留白 15dp 的分隔符焰宣,整個界面效果展現(xiàn)如下(灰色背景部分是整個 ListView 的輪廓):
當(dāng)然,還有其他諸如TransitionDrawable
捕仔、LevelListDrawable
匕积、GradientDrawable
、PictureDrawable
榜跌、PaintDrawable
沒有詳細介紹闪唆,但這幾種一般不是很常用。經(jīng)過前面一些 Drawable 的簡介钓葫,即時我們在工作中需要用到這幾類 Drawable悄蕾,也可以輕松通過查看文檔等方式來學(xué)習(xí)和使用。