前言
本文是本人閱讀《Android開發(fā)藝術(shù)探索》的第6章《Android的Drawable》后的總結(jié)筆記。包含了Android開發(fā)中常用的Drawable,詳解了它們的屬性和用法。
-
本文的目錄結(jié)構(gòu)
- 前言
- Drawable簡介
- Drawable分類
- BitmapDrawable
- NinePatchDrawable
- ShapeDrawable
- LayerDrawable
- StateListDrawable
- LevelListDrawable
- TransitionDrawable
- InsetDrawable
- ScaleDrawable
- ClipDrawable
- 自定義Drawable
Drawable簡介
Drawable表示的是一種可以在Canvas上進(jìn)行繪制的抽象的概念,可以是純顏色,也可以是圖片等。
Drawable一般是通過XML來定義,當(dāng)然也可以通過代碼來創(chuàng)建。
Drawable類是抽象類,它是所有Drawable的基類。
Drawable的
getIntrinsicWidth()和getIntrinsicHeight()獲取其內(nèi)部寬/高。并不是所有Drawable都有內(nèi)部寬/高。圖片所形成的Drawable的內(nèi)部寬/高就是圖片的寬/高。顏色所形成的Drawable就沒有內(nèi)部寬/高的概念了。Drawable是沒有大小概念的,當(dāng)用作View的背景時(shí),會(huì)被拉伸至View的同等大小。
Drawable的使用范圍很單一:一是作為ImageView中的圖像顯示,二是作為View的背景。
Drawable分類
BitmapDrawable
對應(yīng)
<bitmap>標(biāo)簽,幾乎是最簡單的Drawable,表示一張圖片。在實(shí)際開發(fā)中,我們可直接引用原始的圖片,也可通過XML的方式來描述它。
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:antialias="[true | false]"
android:dither="[true | false]"
android:filter="[true | false]"
android:gravity="[top | bottom | left | right |
center_vertical | fill_vertical | center_horizontal |
fill_horizontal | center | fill |
clip_vertical | clip_horizontal]"
android:mipMap="[true | false]"
android:tileMode="[disabled | clamp | repeat | mirror]" />
android:src:圖片的資源id。android:antialias:是否開啟圖片抗鋸齒。開啟后會(huì)讓圖片變得平滑,同時(shí)也會(huì)一定程度上降低圖片的清晰度。android:dither:是否開啟抖動(dòng)效果。開啟后可讓高質(zhì)量的圖片在低質(zhì)量的屏幕上能保持較好的顯示效果。android:filter:是否開啟過濾效果。當(dāng)圖片尺寸被拉伸或壓縮時(shí),開啟過濾效果可保持較好的顯示效果。android:gravity:當(dāng)圖片小于容器的尺寸時(shí),設(shè)置此屬性對圖片進(jìn)行定位。此屬性的可選項(xiàng)較多,也可用|來組合使用。
| 可選項(xiàng) | 含義 |
|---|---|
| top | 將圖片放在容器的頂部,不改變其大小。 |
| bottom | 將圖片放在容器的底部,不改變其大小。 |
| left | 將圖片放在容器的左部,不改變其大小。 |
| right | 將圖片放在容器的右部,不改變其大小。 |
| center_vertical | 使圖片豎直居中,不改變其大小。 |
| center_horizontal | 使圖片水平居中,不改變其大小。 |
| fill_vertical | 圖片豎直方向填充容器 |
| fill_horizontal | 圖片水平方向填充容器 |
| center | 使圖片居中,不改變其大小。 |
| fill | 圖片填充容器,默認(rèn)值。 |
| clip_vertical | 豎直方向的裁剪。 |
| clip_horizontal | 水平方向的裁剪。 |
android:mipMap:是否開啟紋理映射。android:tileMode:平鋪模式。
| 可選項(xiàng) | 含義 |
|---|---|
| disabled | 默認(rèn)值,關(guān)閉平鋪模式。 |
| clamp | 圖片四周的像素會(huì)擴(kuò)展到周圍區(qū)域。 |
| repeat | 簡單的水平和豎直方向上的平鋪效果。 |
| mirror | 在水平和豎直方向上的鏡面投影效果。 |
NinePatchDrawable
表示一張
.9格式的圖片。.9圖片可自動(dòng)地根據(jù)所需的寬/高進(jìn)行相應(yīng)的縮放并保證不失真。在實(shí)際使用中直接引用
.9圖片,也可以通過XML來描述它。
<?xml version="1.0" encoding="utf-8"?>
<nine-patch
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:dither="[true | false]" />
android:src:圖片的資源id。android:dither:是否開啟抖動(dòng)效果。開啟后可讓高質(zhì)量的圖片在低質(zhì)量的屏幕上能保持較好的顯示效果。
ShapeDrawable
- 圖形??墒羌兩膱D形,也可以是具有漸變效果的圖形。
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="[rectangle | oval | line | ring]"
<corners
android:radius="integer"
android:topLeftRaidus="integer"
android:topRightRaidus="integer"
android:bottomLeftRaidus="integer"
android:bottomRightRaidus="integer" />
<gradient
android:angle="integer"
android:centerX="integer"
android:centerY="integer"
android:centerColor="color"
android:endColor="color"
android:gradientRadius="integer"
android:startColor="color"
android:type="[linear | radial | sweep]"
android:useLevel="[true | false]" />
<padding
android:left="integer"
android:top="integer"
android:right="integer"
android:bottom="integer" />
<size
android:width="integer"
android:height="integer" />
<solid
android:color="color" />
<stroke
android:width="integer"
android:color="color"
android:dashWidth="integer"
android:dashGap="integer" />
android:shape:圖形的形狀,可選有rectangle(矩形)、oval(橢圓)、line(橫線)和ring(圓環(huán))。默認(rèn)是rectangle。注意line和ring必須通過<stroke>標(biāo)簽來指定線的寬度和顏色等信息,否則無法達(dá)到預(yù)期的顯示效果。android:shape="ring"有5個(gè)特殊屬性。
| 屬性 | 描述 |
|---|---|
| android:innerRadius | 圓環(huán)的內(nèi)半徑。<Br>會(huì)覆蓋android:innerRadiusRatio。 |
| android:innerRadiusRatio | 內(nèi)半徑占整個(gè)Drawable寬度的比例。 默認(rèn)為9,若為n,則內(nèi)半徑 = 寬度 / n。 |
| android:thickness | 圓環(huán)的厚度。即外半徑減去內(nèi)半徑的大小。<Br>會(huì)覆蓋android:thicknessRatio |
| android:thicknessRatio | 厚度占整個(gè)Drawable寬度的比例。 默認(rèn)為3,若為n,則厚度 = 寬度 / n。 |
| android:useLevel | 常為false,除非它被當(dāng)做是LevelListDrawable。 |
-
<corners>:表示shape的四個(gè)圓角的角度,只適用于矩形。-
android:radius:為四個(gè)角同事設(shè)定相同的角度。優(yōu)先級(jí)比以下4個(gè)屬性要低。 -
android:topLeftRadius:左上角的角度。 -
android:topRightRadius:右上角的角度。 -
android:bottomLeftRadius:左下角的角度。 -
android:bottomRightRadius:右下角的角度。
-
<solid>:純色填充,通過android:color指定填充的顏色。-
<gradient>:漸變效果。與<solid>純色填充是互相排斥的。-
android:angle:漸變的角度。默認(rèn)為0,其值必須為45的倍數(shù)。此角度會(huì)影響漸變的方向,0表示從左到右,90表示從下到上。 -
android:centerX:漸變的中心點(diǎn)的X坐標(biāo)。 -
android:centerY:漸變的中心點(diǎn)的Y坐標(biāo)。 -
android:startColor:漸變的起始色。 -
android:centerColor:漸變的中間色。 -
android:endColor:漸變的結(jié)束色。 -
android:gradient:漸變半徑。僅當(dāng)android:type="radial"時(shí)有效。 -
android:useLevel:一般為false,當(dāng)Drawable作為StateListDrawable時(shí)為true。 -
android:type:漸變的類別。有l(wèi)inear(線性漸變)、radial(徑向漸變)、sweep(掃描線漸變),默認(rèn)為linear。
-
-
<stroke>:描邊。-
android:width:描邊的寬度。 -
android:color:描邊的顏色。 -
android:dashWidth:虛線的寬度。 -
android:dashGap:虛線之間的間隔。
-
<padding>:空白。有top等四個(gè)屬性。<size>:大小。其android:width和android:height分別設(shè)定shape的寬/高。注意,這個(gè)表示的是shape的固有大小,但并不是其最終大小。
LayerDrawable
- 對應(yīng)
<layer-list>標(biāo)簽,表示一種層次化的Drawable集合,通過將不同的Drawable放置在不同的層上面從而達(dá)到一種疊加后的效果。
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
<item
android:drawable="@[package:]drawable/drawable_resource"
android:id="@[+][package:]id/resource_name"
android:top="dimension"
android:right="dimension"
android:bottom="dimension"
android:left="dimension" />
<!-- 其他item -->
</layer-list>
一個(gè)layer-list可包含多個(gè)item,每個(gè)item表示一個(gè)Drawable??稍?code>android:drawable中引用一個(gè)現(xiàn)有的Drawable資源,也可在
<item>中自定義Drawable。默認(rèn)情況下,layer-list中的所有Drawable都會(huì)被縮放至View的大小??稍O(shè)置Drawable相對于View的上下左右偏移量。另外對于bitmap,需要使用其
android:gravity來控制圖片的顯示效果。layer-list有層次的概念,下面的item會(huì)覆蓋上面的item。通過合理的分層,可實(shí)現(xiàn)一些特殊的疊加效果。
-
利用layer-list實(shí)現(xiàn)一個(gè)文本輸入框。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="rectangle"> <solid android:color="#0ac39e" /> </shape> </item> <item android:bottom="6dp"> <shape android:shape="rectangle"> <solid android:color="#ffffff" /> </shape> </item> <item android:bottom="1dp" android:left="1dp" android:right="1dp"> <shape android:shape="rectangle"> <solid android:color="#ffffff" /> </shape> </item> </layer-list>layer-list背景的輸入框
StateListDrawable
- 對應(yīng)
<selector>標(biāo)簽,表示一個(gè)Drawable的集合。每個(gè)Drawable對應(yīng)著View的一種狀態(tài),系統(tǒng)會(huì)根據(jù)View的狀態(tài)來選擇合適的Drawable。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="[true | false]"
android:dither="[true | false]"
android:variablePadding="[true | false]">
<item
android:drawable="@[package:]drawable/drawable_resource"
android:state_pressed="[true | false]"
android:state_focused="[true | false]"
android:state_hovered="[true | false]"
android:state_selected="[true | false]"
android:state_checkable="[true | false]"
android:state_checked="[true | false]"
android:state_enabled="[true | false]"
android:state_activated="[true | false]"
android:state_window_focused="[true | false]" />
<!-- 其他item -->
</selector>
android:constantSize:StateListDrawable的固有大小是否不隨著其狀態(tài)的改變而改變。默認(rèn)為false,即隨著狀態(tài)的改變而改變大小。若為true,則表示其內(nèi)部所有Drawable的固有大小的最大值。android:dither:是否開啟抖動(dòng)效果。android:variblePadding:StateListDrawable的padding是否隨著其狀態(tài)的改變而改變。默認(rèn)為false,表示其內(nèi)部所有Drawable的padding的最大值。若為true,表示會(huì)隨著狀態(tài)的改變而改變。<item>:表示某種狀態(tài)下的一個(gè)具體的Drawable。用android:drawable指定一個(gè)現(xiàn)有Drawable的資源id,剩下的屬性表示的是View的各種狀態(tài)。
| 狀態(tài) | 含義 |
|---|---|
| android:state_pressed | 表示按下狀態(tài)。 |
| android:state_focused | 表示已經(jīng)獲取了焦點(diǎn)。 |
| android:state_selected | 表示用戶選擇了View。 |
| android:state_checked | 表示用戶選中了View。<Br>適用于CheckBox這類在選中和非選中狀態(tài)之間進(jìn)行切換的View。 |
| android:state_enabled | 表示View當(dāng)前處于可用狀態(tài)。 |
- 系統(tǒng)會(huì)按照從上到下的順序查找,直至查找到第一條匹配的item。一般來說,默認(rèn)的item都應(yīng)該放在selector的最后一條,并且不附帶任何的狀態(tài)。
LevelListDrawable
- 對應(yīng)
<level-list>標(biāo)簽,表示一個(gè)Drawable集合,集合中的每個(gè)Drawable都有一個(gè)等級(jí)的概念。根據(jù)不同的等級(jí),LevelListDrawable會(huì)切換為對應(yīng)的Drawable。
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@[package:]drawable/drawable_resource"
android:maxLevel="integer"
android:minLevel="integer" />
<!-- 其他item -->
</level-list>
每個(gè)item表示一個(gè)Drawable,并且有對應(yīng)的等級(jí)范圍,在
maxLevel和minLevel之間。取值范圍為0~10000,默認(rèn)為0。若作為View背景時(shí),可通過Drawable的
setLevel()來設(shè)置不同的等級(jí)來切換具體的Drawable。若作為ImageView的前景,可通過ImageView的setImageLevel()來切換。
TransitionDrawable
- 對應(yīng)于
<transition>標(biāo)簽,用于實(shí)現(xiàn)兩個(gè)Drawable之間的淡入淡出效果。
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@[package:]drawable/drawable_resource"
android:id="@[+][package:]id/resource_name"
android:top="dimension"
android:right="dimension"
android:bottom="dimension"
android:left="dimension" />
<!-- 另一個(gè)item,注意transition只包含兩個(gè)item -->
</transition>
- 常將TransitionDrawable作為View的背景,再調(diào)用它的
startTransition()和reverseTransition()來實(shí)現(xiàn)淡入淡出效果以及它的逆過程。
ImageView ivBg = (ImageView) findViewById(R.id.iv_bg);
TransitionDrawable drawable = (TransitionDrawable) ivBg.getBackground();
drawable.startTransition(1000);
InsetDrawable
- 對應(yīng)
<inset>標(biāo)簽,可將其他Drawable內(nèi)嵌到自己當(dāng)中,并可在四周留出一定的間距。
<?xml version="1.0" encoding="utf-8"?>
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@[package:]drawable/drawable_resource"
android:inset="dimension"
android:insetTop="dimension"
android:insetRight="dimension"
android:insetBottom="dimension"
android:insetLeft="dimension" />
- 當(dāng)一個(gè)View希望自己的背景比自己的實(shí)際區(qū)域小的時(shí)候,可采用InsetDrawable,并通過
inset*屬性來設(shè)置留白。
ScaleDrawable
- 對應(yīng)
<scale>標(biāo)簽,將Drawable縮放到一定比例。
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@[package:]drawable/drawable_resource"
android:scaleGravity="[top | bottom | left | right |
center_vertical | center_horizontal | center |
fill_vertical | fill_horizontal | fill |
clip_vertical | clip_horizontal]"
android:scaleWidth="percentage"
android:scaleHeight="percentage" />
android:scaleGravity:等同于BitmapDrawable的android:gravity。android:scaleWidth和android:scaleHeight:指定Drawable寬/高的縮放比例,以百分比的形式表示。如70%,表示縮放到原來的30%。
- 從ScaleDrawable的
draw()源代碼來看,若等級(jí)為0時(shí),不可見。等級(jí)默認(rèn)為0,所以調(diào)用ScaleDrawable的setLevel()設(shè)置一個(gè)大于0的等級(jí),它才會(huì)可見。
public void draw(Canvas canvas) {
final Drawable d = getDrawable();
if (d != null && d.getLevel() != 0) {
d.draw(canvas);
}
}
- 分析ScaleDrawable的
onBoundsChange()可看出其內(nèi)部mDrawable的大小和等級(jí)以及縮放比例的關(guān)系。
// iw常為0
final int iw = min ? d.getIntrinsicWidth() : 0;
// 1. MAX_LEVEL為10000,即等級(jí)的取值范圍為0~10000。
// 2. 計(jì)算得到的最終w為其內(nèi)部mDrawable最后顯示的寬度。因?yàn)槭?=,所以右邊算數(shù)得到的值越大,則最終w就越小。
// 3. level越大,其內(nèi)部mDrawable就越大。
// 4. ScaleDrawable的XML中所定義的縮放比例越大,即mState.mScaleWidth越大,則內(nèi)部mDrawable就越小。
w -= (int) ((w - iw) * (MAX_LEVEL - level) * mState.mScaleWidth / MAX_LEVEL);
ClipDrawable
- 對應(yīng)
<clip>標(biāo)簽,用來裁剪另一個(gè)Drawable。
<?xml version="1.0" encoding="utf-8"?>
<clip
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@[package:]drawable/drawable_resource"
android:clipOrientation="[vertical | horizontal]"
android:gravity="[top | bottom | left | right |
center_vertical | center_horizontal | center |
fill_vertical | fill_horizontal | fill |
clip_vertical | clip_horizontal]" />
android:clipOrientation:表示裁剪方向,可選為水平和豎直。android:gravity:表示對齊方式,需要和clipOrientation一起發(fā)揮作用。它的取值有下表各種選項(xiàng),可用|來組合使用。
| 選項(xiàng) | 含義 |
|---|---|
| top | 放在容器的頂部,不改變大小。 若為豎直裁剪,則從底部開始裁剪。 |
| bottom | 放在容器的底部,不改變大小。 若為豎直裁剪,則從頂部開始裁剪。 |
| left | 這是默認(rèn)值。放在容器的左邊,不改變大小。 若為水平裁剪,則從右邊開始裁剪。 |
| right | 放在容器的右邊,不改變大小。 若為水平裁剪,則從左邊開始裁剪。 |
| center_vertical | 在容器中豎直居中,不改變大小。 若為豎直裁剪,則從上下同時(shí)裁剪。 |
| center_horizontal | 在容器中水平居中,不改變大小。 若為水平裁剪,則從左右同時(shí)裁剪。 |
| center | 居中,不改變大小。 若為豎直裁剪,則從上下同時(shí)裁剪。 若為水平裁剪,則從左右同時(shí)裁剪。 |
| fill_vertical | 在豎直方向上填充容器。 若為豎直裁剪,僅當(dāng)?shù)燃?jí)為0時(shí)才能有裁剪行為。 |
| fill_horizontal | 在水平方向上填充容器。 若為水平裁剪,僅當(dāng)?shù)燃?jí)為0時(shí)才能有裁剪行為。 |
| fill | 完全填充容器。 僅當(dāng)?shù)燃?jí)為0時(shí)才能有裁剪行為。 |
| clip_vertical | 附加選項(xiàng),表示豎直方向的裁剪。較少使用。 |
| clip_horizontal | 附加選項(xiàng),表示水平方向的裁剪。較少使用。 |
- ClipDrawable的裁剪程度由level控制,調(diào)用
setLevel()可修改此值。其取值范圍為0~10000。0表示完全裁剪,即整個(gè)Drawable都不可見;而10000表示不裁剪。也可將level理解為可見區(qū)域大小,0表示可見區(qū)最小,10000表示可見區(qū)最大。
自定義Drawable
Drawable的工作原理很簡單,其核心就是
draw()。系統(tǒng)會(huì)調(diào)用Drawable的draw()來繪制View的背景或ImageView的圖像,于是我們可通過重寫其draw()來實(shí)現(xiàn)自定義Drawable。通常我們沒有必要去自定義Drawable,因?yàn)闊o法在XML中使用自定義Drawable,這就降低了其使用范圍。
創(chuàng)建自定義Drawable,必須重寫其
draw()、setAlpha()、setColorFilter()、getOpacity()等方法。以下為自定義Drawable示例:
/**
* 自定義Drawable
* Created by daking on 16/9/15.
*/
public class CustomDrawable extends Drawable {
private Paint mPaint;
public CustomDrawable(int color) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(color);
}
@Override
public void draw(Canvas canvas) {
final Rect rect = getBounds();
float cx = rect.exactCenterX();
float cy = rect.exactCenterY();
canvas.drawCircle(cx, cy, Math.min(cx, cy), mPaint);
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
invalidateSelf();
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
CustomDrawable drawable = new CustomDrawable(Color.BLUE);
imageView.setBackgroundDrawable(drawable);
若自定義的Drawable有固有大小時(shí),要重寫
getIntrinsicWidth()和getIntrinsicHeight()。因?yàn)檫@兩個(gè)方法會(huì)影響到View的wrap_content布局。注意,Drawable的內(nèi)部大小不等于Drawable的實(shí)際區(qū)域大小。可通過
getBounds()獲得Drawable的實(shí)際區(qū)域大小,一般與它的View尺寸相同。
