Android中常用的Drawable

前言

  • 本文是本人閱讀《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è)圓角的角度,只適用于矩形。

    1. android:radius:為四個(gè)角同事設(shè)定相同的角度。優(yōu)先級(jí)比以下4個(gè)屬性要低。
    2. android:topLeftRadius:左上角的角度。
    3. android:topRightRadius:右上角的角度。
    4. android:bottomLeftRadius:左下角的角度。
    5. android:bottomRightRadius:右下角的角度。
  • <solid>:純色填充,通過android:color指定填充的顏色。

  • <gradient>:漸變效果。與<solid>純色填充是互相排斥的。

    1. android:angle:漸變的角度。默認(rèn)為0,其值必須為45的倍數(shù)。此角度會(huì)影響漸變的方向,0表示從左到右,90表示從下到上。
    2. android:centerX:漸變的中心點(diǎn)的X坐標(biāo)。
    3. android:centerY:漸變的中心點(diǎn)的Y坐標(biāo)。
    4. android:startColor:漸變的起始色。
    5. android:centerColor:漸變的中間色。
    6. android:endColor:漸變的結(jié)束色。
    7. android:gradient:漸變半徑。僅當(dāng)android:type="radial"時(shí)有效。
    8. android:useLevel:一般為false,當(dāng)Drawable作為StateListDrawable時(shí)為true。
    9. android:type:漸變的類別。有l(wèi)inear(線性漸變)、radial(徑向漸變)、sweep(掃描線漸變),默認(rèn)為linear。
  • <stroke>:描邊。

    1. android:width:描邊的寬度。
    2. android:color:描邊的顏色。
    3. android:dashWidth:虛線的寬度。
    4. android:dashGap:虛線之間的間隔。
  • <padding>:空白。有top等四個(gè)屬性。

  • <size>:大小。其android:widthandroid: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í)范圍,在maxLevelminLevel之間。取值范圍為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:scaleWidthandroid: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尺寸相同。

我的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,954評論 25 709
  • Drawable有很多種,它們都表示一種圖像的概念,但它們又不全是圖片,通過顏色也可以構(gòu)造出各式各樣的圖像的效果。...
    cooperise閱讀 539評論 0 7
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,147評論 22 665
  • 概述 今天我們來探究一下android的樣式。其實(shí),幾乎所有的控件都可以使用 background屬性去引用自定義...
    CokeNello閱讀 5,104評論 1 19
  • 倒不是我的感情多泛濫,但的確是想抓住一個(gè)人,一個(gè)值得的人傾盡所有去對他。哪怕?lián)Q來的還是殊途,卻也不曾妄想過有...
    難像閱讀 209評論 0 0

友情鏈接更多精彩內(nèi)容