Drawable Resources與Color State List Resource

概述

Android把任何可繪制在屏幕上的圖形圖像都稱為drawable 資源,你可以通過(guò)類似getDrawable(int)的API來(lái)獲取drawable資源,你也可以通過(guò)類似android:drawable 、 android:icon的屬性將drawable 資源應(yīng)用到其他的XML資源中。Drawable表示的是一種可以在Canvas上進(jìn)行繪制的抽象的概念、它的種類有很多,它們都表示一種圖像的概念,通過(guò)圖片或者顏色可以構(gòu)造出各式各樣的圖像的效果,也可以通過(guò)繼承Drawable類自定義Drawable來(lái)構(gòu)建圖像的效果。
我們經(jīng)常會(huì)接觸到的drawable: state list drawable、shape drawable、 layer list drawable、ripple drawable、inset drawable以及nine patch drawable。前5個(gè)drawable通常定義在XML布局文件中,因此我們統(tǒng)一將它們歸屬為XML drawable類別。這6中常用的drawable與Drawable類之間的關(guān)系如下圖所示:


Drawable類繼承關(guān)系圖

下面我會(huì)講解上面提到的六種常用的Drawable和Color State List Resource,最后再講解一下自定義Drawable。

預(yù)備知識(shí)

1 關(guān)于Image Dithering(圖像抖動(dòng))的介紹
Image Dithering(圖像抖動(dòng)) 是 Dithering(抖動(dòng))在數(shù)字圖片處理中的一種應(yīng)用。一般情況下是指一種使數(shù)字圖片在降低色深的情況下,呈現(xiàn)最佳展現(xiàn)的技術(shù)。比如將24bit色深的圖像使用8bit色深來(lái)展現(xiàn)。
例圖:


原圖

原圖降低色深,沒(méi)有加抖動(dòng)處理

原圖降低色深,加抖動(dòng)處理

圖像抖動(dòng)技術(shù)常用的算法是 Floyd–Steinberg dithering.
編程應(yīng)用: 在Android應(yīng)用開(kāi)發(fā)中,使用 android:dither 屬性來(lái)設(shè)置圖像是否應(yīng)用抖動(dòng)處理。

幾種常用的XML drawable

1 shape drawable
使用XML文件來(lái)定義幾何圖形,可以理解為通過(guò)顏色來(lái)構(gòu)造的幾何圖形,它既可以是純色的幾何圖形,也可以是具有漸變效果的幾何圖形。對(duì)應(yīng)GradientDrawable類。

file location:
    res/drawable/filename.xml
    The filename is used as the resource ID.
compiled resource datatype:
    Resource pointer to a GradientDrawable.
resource reference:
    In Java: R.drawable.filename
In XML: @[package:]drawable/filename

語(yǔ)法:

<?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:topLeftRadius="integer"
        android:topRightRadius="integer"
        android:bottomLeftRadius="integer"
        android:bottomRightRadius="integer" />
    <gradient
        android:angle="integer"
        android:centerX="float"
        android:centerY="float"
        android:centerColor="integer"
        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" />
</shape>

1.1 <shape>節(jié)點(diǎn)元素
該節(jié)點(diǎn)元素一定被用作根元素。
<shape>節(jié)點(diǎn)元素的常用屬性:
1> android:shape
定義shape drawable的類型,可用的有效值如下:

Value Desciption
"rectangle" A rectangle that fills the containing View. This is the default shape.
"oval" An oval shape that fits the dimensions of the containing View.
"line" A horizontal line that spans the width of the containing View. This shape requires the <stroke> element to define the width of the line.
"ring" A ring shape.

line和ring這兩個(gè)類型必須要通過(guò)stroke節(jié)點(diǎn)元素來(lái)指定線的寬度和顏色等信息,否者無(wú)法達(dá)到預(yù)期的顯示效果。
2> 只有當(dāng)android:shape="ring"時(shí),以下的屬性才被使用:

android:innerRadius
Dimension 內(nèi)環(huán)的半徑,可直接使用尺寸值或者引用尺寸資源。

android:innerRadiusRatio
Float 內(nèi)環(huán)半徑與環(huán)寬度的比例。例如,如果android:innerRadiusRatio="5",則內(nèi)環(huán)半徑等于drawable寬度的5分之1,這個(gè)值會(huì)被android:innerRadius屬性值重寫。默認(rèn)值為3。

android:thickness
Dimension 環(huán)的厚度,可直接使用尺寸值或者引用尺寸資源。

android:thicknessRatio
Float 環(huán)的厚度與環(huán)寬度的比例。例如,如果android:thicknessRatio="2",則環(huán)的厚度等于drawable的寬度除以2。這個(gè)值會(huì)被android:innerRadius屬性值重寫。默認(rèn)值是9。

android:useLevel
Boolean 一般都應(yīng)該使用false,否者有可能無(wú)法達(dá)到預(yù)期的顯示效果,除非它被當(dāng)做LevelListDrawable來(lái)使用。

1.2 <corners>節(jié)點(diǎn)元素
用來(lái)為shape drawable創(chuàng)建圓角,僅適用于shape drawable是矩形時(shí),即只有<shape>節(jié)點(diǎn)中android:shape屬性為rectangle時(shí)。
<corners>節(jié)點(diǎn)元素的常用屬性:

android:radius
Dimension 所有圓角的半徑,可直接使用尺寸值或者引用尺寸資源。每一個(gè)圓角的半徑值會(huì)被如下的屬性值重寫。

android:topLeftRadius
Dimension 左上圓角的半徑,可直接使用尺寸值或者引用尺寸資源。

android:topRightRadius
Dimension 右上圓角的半徑,可直接使用尺寸值或者引用尺寸資源。

android:bottomLeftRadius
Dimension 左下圓角的半徑,可直接使用尺寸值或者引用尺寸資源。

android:bottomRightRadius
Dimension 右下圓角的半徑,可直接使用尺寸值或者引用尺寸資源。

注意:圓角的半徑必須大于1,否則沒(méi)有圓角。如果你想要指定某個(gè)角不是圓角,一個(gè)解決方法是使用android:radius屬性去給圓角半徑設(shè)置一個(gè)大于1的默認(rèn)值,然后重寫你不想是圓角的對(duì)應(yīng)屬性值為0dp。

1.3 <gradient>節(jié)點(diǎn)元素
為shape drawable指定一種漸變顏色。它與<solid>節(jié)點(diǎn)元素是互相排斥的,其中solid表示純色填充,而gradient則表示漸變效果。
<gradient>節(jié)點(diǎn)元素的常用屬性:

android:angle
Integer 顏色漸變的方向,0 is left to right, 90 is bottom to top.必須是45的倍數(shù). Default is 0.

android:type
Keyword 漸變圖案的類型,可用的有效值如下:
android:startColor
Color顏色漸變的開(kāi)始顏色,為十六進(jìn)制顏色值值或者引用Color資源。

android:endColor
Color顏色漸變的結(jié)束顏色,為十六進(jìn)制顏色值值或者引用Color資源。

android:centerColor
Color顏色漸變的中間顏色,為十六進(jìn)制顏色值或者引用Color資源。

android:centerX
Float (0 - 1.0) 相對(duì)X的漸變位置,默認(rèn)值為0.5。在type為linear時(shí)而且只有centerColor被設(shè)置時(shí)該屬性才會(huì)起作用。

android:centerY
Float (0 - 1.0) 相對(duì)Y的漸變位置,默認(rèn)值為0.5。在type為linear時(shí)而且只有centerColor被設(shè)置時(shí)該屬性才會(huì)起作用。

android:gradientRadius
Float 漸變的半徑,單位應(yīng)該是像素點(diǎn). 僅當(dāng)android:type="radial"時(shí)有效.

android:useLevel
Boolean 一般為false,當(dāng)Drawable作為L(zhǎng)evelListDrawable使用時(shí)為true.

舉例說(shuō)明:
例一:
顏色從透明到不透明漸變。



運(yùn)行結(jié)果如下:



例二:

運(yùn)行結(jié)果如下:

例三:



運(yùn)行結(jié)果如下:

例四:

運(yùn)行結(jié)果如下:

例五:

運(yùn)行結(jié)果如下:

1.4 <solid>節(jié)點(diǎn)元素
這個(gè)節(jié)點(diǎn)元素表示純色填充,通過(guò)android:color即可指定shape drawable中填充的顏色。

1.5 <stroke>節(jié)點(diǎn)元素
描邊。
<stroke>節(jié)點(diǎn)元素的常用屬性:

android:width="2dp"   描邊的寬度。
android:color   描邊的顏色。

我們還可以把描邊弄成虛線的形式,設(shè)置方式為:
android:dashWidth="5dp"   表示'-'這樣一個(gè)橫線的寬度.
android:dashGap="3dp"     表示'-'之間隔開(kāi)的距離。

1.6 <padding>節(jié)點(diǎn)元素
用來(lái)設(shè)置包含該shape drawable的View的padding
<padding>節(jié)點(diǎn)元素的常用屬性:

android:left
Dimension  Left padding,可直接使用尺寸值或者引用尺寸資源。

android:top
Dimension  Top padding,可直接使用尺寸值或者引用尺寸資源。

android:right
Dimension  Right padding,可直接使用尺寸值或者引用尺寸資源。

android:bottom
Dimension  Bottom padding,可直接使用尺寸值或者引用尺寸資源。

1.7 <size>節(jié)點(diǎn)元素
shape drawable的固有大小。
<size>節(jié)點(diǎn)元素的常用屬性:

android:height
Dimension  shape drawable的固有高度,可直接使用尺寸值或者引用尺寸資源。

android:width
Dimension  shape drawable的固有寬度,可直接使用尺寸值或者引用尺寸資源。

上面的兩個(gè)屬性表示的是shape drawable的固有寬高,但是一般來(lái)說(shuō)它并不是shape drawable最終顯示的寬高,這個(gè)有點(diǎn)抽象,但是我們要明白,對(duì)于shape drawable來(lái)說(shuō)它并沒(méi)有寬高的概念,作為View的背景shape drawable會(huì)自適應(yīng)View的寬高。我們知道Drawable的兩個(gè)方法getIntrisicWidth和getIntrinsicHeight表示的是Drawable的固有寬高,對(duì)于有些Drawable比如BitmapDrawable,它的固有寬高就是圖片的尺寸;而對(duì)于shape drawable來(lái)說(shuō),默認(rèn)情況下他是沒(méi)有固有寬高的概念的,這個(gè)時(shí)候方法getIntrisicWidthhe和getIntrinsicHeight會(huì)返回-1,但是如果通過(guò)<size>標(biāo)簽來(lái)指定寬高信息,那么這個(gè)時(shí)候shape drawable就有了所謂的固有寬高;因此,總結(jié)來(lái)說(shuō),<size>標(biāo)簽設(shè)置的寬高就是shape drawable的固有寬高,但是作為View的背景時(shí),shape drawable還會(huì)被拉伸或者縮小為View的大小。

2 inset drawable
當(dāng)一個(gè)View希望自己的背景比自己的實(shí)際區(qū)域小的時(shí)候,這非常有用。對(duì)應(yīng)InsetDrawable類。

file location:
    res/drawable/filename.xml
    The filename is used as the resource ID.
compiled resource datatype:
    Resource pointer to a InsetDrawable.
resource reference:
    In Java: R.drawable.filename
    In XML: @[package:]drawable/filename 

語(yǔ)法:

<?xml version="1.0" encoding="utf-8"?>
<inset
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:insetTop="dimension"
    android:insetRight="dimension"
    android:insetBottom="dimension"
    android:insetLeft="dimension" />

2.1 <inset>節(jié)點(diǎn)元素:
<inset>節(jié)點(diǎn)元素的常用屬性:

android:drawable:
    Drawable resource. Required. Reference to a drawable resource to be inset.
android:insetTop
    Dimension. inset drawable與使用該inset drawable的View的頂部距離, as a dimension value or dimension resource
android:insetRight
    Dimension. inset drawable與使用該inset drawable的View的右邊距離, as a dimension value or dimension resource
android:insetBottom
    Dimension. inset drawable與使用該inset drawable的View的底部距離, as a dimension value or dimension resource
android:insetLeft
    Dimension. inset drawable與使用該inset drawable的View的左邊距離, as a dimension value or dimension resource

3 layer list drawable
layer list drawable是一個(gè)管理著一個(gè)Drawable對(duì)象數(shù)組的LayerDrawable類的對(duì)象,數(shù)組中的每一個(gè)Drawable對(duì)象按照下標(biāo)的順序進(jìn)行繪制,因此數(shù)組中的最后一個(gè)Drawable對(duì)象被繪制在最上面。
每一個(gè)Drawable是通過(guò)一個(gè)<item>元素來(lái)表示,每一個(gè)<item>元素都定義在<layer-list> 元素中。

FILE LOCATION:
    res/drawable/filename.xml
    The filename is used as the resource ID.
COMPILED RESOURCE DATATYPE:
    Resource pointer to a LayerDrawable.
RESOURCE REFERENCE:
    In Java: R.drawable.filename
    In XML: @[package:]drawable/filename

語(yǔ)法:

<?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" />
</layer-list>

一個(gè)layer list drawable中可以包含多個(gè)item,每個(gè)item表示一個(gè)Drawable。item的結(jié)構(gòu)比較簡(jiǎn)單,比較常用的屬性有android:top、android:right、android:bottom和android:left,他們分別表示Drawable相對(duì)于View的上下左右的偏移量,單位為像素。另外,我們可以通過(guò)android:drawable來(lái)引用一個(gè)已有的Drawable資源,當(dāng)然也可以在item中自定義Drawable。

4 ripple drawable
該drawable是在api21(L)時(shí)被引進(jìn)的,可以實(shí)現(xiàn)觸摸反饋的效果(波浪的效果)。對(duì)應(yīng)RippleDrawable類,繼承LayerDrawable(即繼承l(wèi)ayer list drawable的所有特點(diǎn))。

4.1 針對(duì)ImageView的background屬性
下面三個(gè)例子是針對(duì)ImageView的background屬性的:
對(duì)于三個(gè)例子的總結(jié)(針對(duì)background屬性):
波浪最大區(qū)域是該View的外接圓

例一 If no child layers or mask is specified and the ripple is set as a View background , the ripple will be drawn atop the first available parent background within the View's hierarchy. In this case, the drawing region may extend outside of the Drawable bounds.
我的翻譯 :在ripple中沒(méi)有item子節(jié)點(diǎn)或者包含有一個(gè)id被指定為@android:id/mask的item并且將該ripple drawable設(shè)置為View的background屬性值時(shí),波浪動(dòng)畫(huà)將會(huì)繪制在視圖層級(jí)結(jié)構(gòu)中第一個(gè)可見(jiàn)的該View的父視圖之上,在這種情況下,波浪動(dòng)畫(huà)可能會(huì)越過(guò)View區(qū)域,波浪動(dòng)畫(huà)最大區(qū)域是該View區(qū)域的外接圓,但是不會(huì)超過(guò)第一個(gè)可見(jiàn)父視圖的區(qū)域。

舉例代碼如下:

/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_margin="30dp"
        android:background="@drawable/beauty2" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/iv_background"
            android:clickable="true"
            android:layout_marginTop="30dp"
            android:padding="30dp"
            android:src="@drawable/beauty4" />
    </LinearLayout>

</LinearLayout>

/TestDrawable/res/drawable/iv_background1.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff00ff00" >

</ripple>

運(yùn)行結(jié)果如下:


例二
在例一的基礎(chǔ)上在ripple根節(jié)點(diǎn)上添加一個(gè)item項(xiàng),相當(dāng)于ripple drawable當(dāng)前擁有一個(gè)Drawable圖層并且當(dāng)將該ripple drawable設(shè)置為View的background屬性值時(shí)該Drawable圖層可以限制波浪動(dòng)畫(huà)不會(huì)越過(guò)View區(qū)域,波浪動(dòng)畫(huà)將被繪制在該View區(qū)域上,在這種情況下,波浪動(dòng)畫(huà)不會(huì)越過(guò)View區(qū)域,波浪最大區(qū)域是該View區(qū)域的外接圓,但是波浪動(dòng)畫(huà)不會(huì)越過(guò)View區(qū)域。

舉例代碼如下:

/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_margin="30dp"
        android:background="@drawable/beauty2" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/iv_background2"
            android:clickable="true"
            android:layout_marginTop="30dp"
            android:padding="30dp"
            android:src="@drawable/beauty4" />
    </LinearLayout>

</LinearLayout>

/TestDrawable/res/drawable/iv_background2.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff00ff00" >

    <item android:drawable="@android:color/black"/>

</ripple>

運(yùn)行結(jié)果如下:


例三
在例二中添加的item節(jié)點(diǎn)中添加android:id="@android:id/mask",相當(dāng)于當(dāng)將該ripple drawable設(shè)置為View的background屬性值時(shí)該item節(jié)點(diǎn)定義的Drawable圖層僅僅是用來(lái)限制波浪動(dòng)畫(huà)不會(huì)越過(guò)View區(qū)域。波浪動(dòng)畫(huà)將被繪制在該View區(qū)域上,在這種情況下,波浪動(dòng)畫(huà)不會(huì)越過(guò)View區(qū)域,波浪最大區(qū)域是該View區(qū)域的外接圓,但是波浪動(dòng)畫(huà)不會(huì)超過(guò)View區(qū)域。
舉例代碼如下:

/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="30dp"
        android:background="@drawable/beauty2"
        android:orientation="vertical" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/iv_background3"
            android:clickable="true"
            android:layout_marginTop="30dp"
            android:padding="30dp"
            android:src="@drawable/beauty4" />
    </LinearLayout>

</LinearLayout>

/TestDrawable/res/drawable/iv_background3.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff00ff00" >

    <item android:id="@android:id/mask"
        android:drawable="@android:color/black"/>

</ripple>

運(yùn)行結(jié)果如下:


4.2 針對(duì)ImageView的src屬性的

下面三個(gè)例子是針對(duì)ImageView的src屬性的:
對(duì)于三個(gè)例子的總結(jié)(針對(duì)src屬性):
波浪最大區(qū)域是該View內(nèi)容的外接圓

例一:
在ripple中沒(méi)有item子節(jié)點(diǎn)或者包含有一個(gè)id被指定為@android:id/mask的item并且將該ripple drawable設(shè)置為View的src屬性值時(shí),波浪動(dòng)畫(huà)將會(huì)繪制在該View區(qū)域上,在這種情況下,波浪動(dòng)畫(huà)可能會(huì)越過(guò)View內(nèi)容區(qū)域,波浪動(dòng)畫(huà)最大區(qū)域是該View內(nèi)容區(qū)域的外接圓,但是不會(huì)超過(guò)View區(qū)域。
舉例代碼如下:

/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="30dp"
        android:background="@drawable/beauty2"
        android:orientation="vertical" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/beauty4"
            android:clickable="true"
            android:layout_marginTop="30dp"
            android:padding="30dp"
            android:src="@drawable/iv_background1" />
    </LinearLayout>

</LinearLayout>

/TestDrawable/res/drawable/iv_background1.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff00ff00" >

</ripple>

運(yùn)行結(jié)果如下:


例二:
在例一的基礎(chǔ)上在ripple根節(jié)點(diǎn)上添加一個(gè)item項(xiàng),相當(dāng)于ripple drawable當(dāng)前擁有一個(gè)Drawable圖層并且當(dāng)將該ripple drawable設(shè)置為View的src屬性值時(shí)該Drawable圖層可以限制波浪動(dòng)畫(huà)不會(huì)越過(guò)View內(nèi)容區(qū)域,波浪動(dòng)畫(huà)將被繪制在該View內(nèi)容區(qū)域上,在這種情況下,波浪動(dòng)畫(huà)不會(huì)越過(guò)View內(nèi)容區(qū)域,波浪最大區(qū)域是該View內(nèi)容區(qū)域的外接圓,但是波浪動(dòng)畫(huà)不會(huì)越過(guò)View內(nèi)容區(qū)域。
舉例代碼如下:

/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="30dp"
        android:background="@drawable/beauty2"
        android:orientation="vertical" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/beauty4"
            android:clickable="true"
            android:layout_marginTop="30dp"
            android:padding="30dp"
            android:src="@drawable/iv_background2" />
    </LinearLayout>

</LinearLayout>

/TestDrawable/res/drawable/iv_background2.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff00ff00" >

    <item android:drawable="@android:color/black"/>

</ripple>

運(yùn)行結(jié)果如下:


例三
在例二中添加的item節(jié)點(diǎn)中添加android:id="@android:id/mask",相當(dāng)于當(dāng)將該ripple drawable設(shè)置為View的src屬性值時(shí)該item節(jié)點(diǎn)定義的Drawable圖層僅僅是用來(lái)限制波浪動(dòng)畫(huà)不會(huì)越過(guò)View內(nèi)容區(qū)域。波浪動(dòng)畫(huà)將被繪制在該View內(nèi)容區(qū)域上,在這種情況下,波浪動(dòng)畫(huà)不會(huì)越過(guò)View內(nèi)容區(qū)域,波浪最大區(qū)域是該View內(nèi)容區(qū)域的外接圓,但是波浪動(dòng)畫(huà)不會(huì)超過(guò)View內(nèi)容區(qū)域。
舉例代碼如下:

/TestDrawable/res/layout/fragment_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="30dp"
        android:background="@drawable/beauty2"
        android:orientation="vertical" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/beauty4"
            android:clickable="true"
            android:layout_marginTop="30dp"
            android:padding="30dp"
            android:src="@drawable/iv_background3" />
    </LinearLayout>

</LinearLayout>

/TestDrawable/res/drawable/iv_background3.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ff00ff00" >

    <item android:id="@android:id/mask"
        android:drawable="@android:color/black"/>

</ripple>

運(yùn)行結(jié)果如下:


5 state list drawable
state list drawable是一個(gè)管理著一個(gè)Drawable對(duì)象集合的StateListDrawable類的對(duì)象。集合中的每一個(gè)Drawable對(duì)象對(duì)應(yīng)View的一種狀態(tài),這樣系統(tǒng)會(huì)根據(jù)View的狀態(tài)來(lái)選擇合適的Drawable。

FILE LOCATION:
    res/drawable/filename.xml
    The filename is used as the resource ID.
COMPILED RESOURCE DATATYPE:
    Resource pointer to a StateListDrawable.
RESOURCE REFERENCE:
    In Java: R.drawable.filename
    In XML: @[package:]drawable/filename

語(yǔ)法:

<?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"] />
</selector>

5.1 <selector>節(jié)點(diǎn)元素:
Required 該節(jié)點(diǎn)元素一定被作為根元素。包含一個(gè)或多個(gè)<item>元素。
<selector>節(jié)點(diǎn)元素的常用屬性:

android:constantSize
state list drawable的固有大小是否不隨著View狀態(tài)的改變而改變,因?yàn)閂iew狀態(tài)會(huì)導(dǎo)致
state list drawable切換到具體的Drawable,而不同的Drawable具有不同的固有大小。
true表示state list drawable的固有大小保持不變,這時(shí)state list drawable的固有大小是
內(nèi)部所有Drawable的固有大小的最大值,false則會(huì)隨著View狀態(tài)的改變而改變。
此選項(xiàng)的默認(rèn)值是false。

android:dither
是否開(kāi)啟抖動(dòng)效果,開(kāi)啟此選項(xiàng)可以讓圖片在低質(zhì)量的屏幕上仍然獲得較好的顯示效果。
此選項(xiàng)默認(rèn)值為true。

android:variablePadding
state list drawable的padding表示是否隨著View狀態(tài)的改變而改變,true表示會(huì)隨著View狀態(tài)的改變而改變,false表示state list drawable的padding是內(nèi)部所有Drawable的padding的最大值。此選項(xiàng)默認(rèn)值為false,并且不建議開(kāi)啟此選項(xiàng)。

5.2 <item>節(jié)點(diǎn)元素:
通過(guò)屬性來(lái)描述在某個(gè)狀態(tài)下使用某個(gè)drawable。必須是<selector>節(jié)點(diǎn)的子節(jié)點(diǎn)。
<item>節(jié)點(diǎn)元素的常用屬性:

android:drawable:
Drawable resource. Required.引用一個(gè)drawable resource.

android:state_pressed
Boolean. 如果屬性值為true,則使用該state list drawable的組件被按下時(shí)該item被使用;如果屬性值為false,則使用該state list drawable的組件沒(méi)有被按下時(shí)該item被使用;

android:state_focused
Boolean. 如果屬性值為true,則使用該state list drawable的組件獲取input focus時(shí)該item被使用;如果屬性值為false,則使用該state list drawable的組件沒(méi)有獲取input focus時(shí)該item被使用;

android:state_selected
Boolean. 如果屬性值為true,則使用該state list drawable的組件被選擇時(shí)該item被使用;如果屬性值為false,則使用該state list drawable的組件沒(méi)有被選擇時(shí)該item被使用。例如當(dāng)在幾個(gè)tab間切換時(shí),被選中的tab和沒(méi)有被選中的tab的背景顏色設(shè)置為不同的
github上的PagerSlidingTabStrip應(yīng)該利用到該屬性。

android:state_checkable
Boolean. 如果屬性值為true,"true" if this item should be used when the object is checkable; "false" if this item should be used when the object is not checkable. (Only useful if the object can transition between a checkable and non-checkable widget.)

android:state_checked
Boolean. 如果屬性值為true,則使用該state list drawable的組件被checked時(shí)該item被使用;如果屬性值為false,則使用該state list drawable的組件沒(méi)有被checked時(shí)該item被使用。

android:state_activated
Boolean. "true" if this item should be used when the object is activated as the persistent selection (such as to "highlight" the previously selected list item in a persistent navigation view); "false" if it should be used when the object is not activated.
Introduced in API level 11

注意:Android系統(tǒng)會(huì)從上到下進(jìn)行查找,直到查找到第一條匹配的item,并且如果state list drawable中的某一項(xiàng)沒(méi)有設(shè)置狀態(tài)屬性,則該項(xiàng)會(huì)被應(yīng)用每一次,因此默認(rèn)Item(沒(méi)有設(shè)置狀態(tài)屬性)應(yīng)該放到最后一項(xiàng)。

nine patch drawable

9-patch圖像是一種特殊格式的文件,因?yàn)锳ndroid知道圖像的哪些部分可以拉伸縮放,哪些部分不可以。經(jīng)適當(dāng)處理后,可保證背景圖的邊角與工具創(chuàng)建的圖像保持一致性。
為什么要叫做9-patch呢? 9-patch可將圖像分成3× 3的網(wǎng)格,即由 9部分或9 patch組成的網(wǎng)格。網(wǎng)格角落的patch不會(huì)被縮放,邊緣部分的4個(gè)patch只按一個(gè)維度縮放,而中間部分則同時(shí)按兩個(gè)維度縮放,如下圖所示:



9-patch圖像和普通的png圖像基本相同,但以下兩點(diǎn)除外: 9-patch圖像文件名是以.9.png結(jié)尾的,圖像邊緣具有一個(gè)像素寬度的邊框,頂部以及左邊框黑線用來(lái)標(biāo)記圖像的可伸縮區(qū)域,底部以及右邊框黑線用于標(biāo)記9-patch圖像的可填充內(nèi)容區(qū)域。 可填充內(nèi)容區(qū)域是內(nèi)容(通常是文字)繪制的地方。若不設(shè)置可填充內(nèi)容區(qū)域,則默認(rèn)與可拉伸區(qū)域保持一致。對(duì)應(yīng)NinePatchDrawable類。可通過(guò)如下兩種方式使用9-path文件。
1 直接使用本地的9-path資源文件

file location:
    res/drawable/filename.9.png
    The filename is used as the resource ID.
compiled resource datatype:
    Resource pointer to a NinePatchDrawable.
resource reference:
    In Java: R.drawable.filename
    In XML: @[package:]drawable/filename 

舉例如下:

<Button
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:background="@drawable/myninepatch" />

2 通過(guò)xml文件間接使用本地9-path資源文件

file location:
    res/drawable/filename.xml
    The filename is used as the resource ID.
compiled resource datatype:
    Resource pointer to a NinePatchDrawable.
resource reference:
    In Java: R.drawable.filename
    In XML: @[package:]drawable/filename 

語(yǔ)法:

<?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"] />

< nine-patch>節(jié)點(diǎn)元素:
< nine-patch>節(jié)點(diǎn)元素的常用屬性:

android:src:
Drawable resource. Required.引用一個(gè)Nine-Patch File

android:dither
當(dāng)位圖不具有與屏幕相同的像素結(jié)構(gòu)時(shí),是否啟用或禁用抖動(dòng)效果(關(guān)于抖動(dòng)效果的說(shuō)明可以參考預(yù)備知識(shí)部分)。

舉例如下:

<?xml version="1.0" encoding="utf-8"?>
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/myninepatch"
    android:dither="false" />

自定義Drawable

Drawable的使用方式很單一,一種方式用是作View背景的(也是大多數(shù)情況下),另一種方式是作為ImageView的內(nèi)容顯示。在我的另一篇文章Android自定義View中再講解View的繪制過(guò)程中提到過(guò)繪制View的第一步就是繪制背景,下面看一下View類中繪制背景的源碼:

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

    setBackgroundBounds();

    // Attempt to use a display list if requested.
    if (canvas.isHardwareAccelerated() && mAttachInfo != null
            && mAttachInfo.mHardwareRenderer != null) {
        mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);

        final RenderNode renderNode = mBackgroundRenderNode;
        if (renderNode != null && renderNode.isValid()) {
            setBackgroundRenderNodeProperties(renderNode);
            ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
            return;
        }
    }

    final int scrollX = mScrollX;
    final int scrollY = mScrollY;
    if ((scrollX | scrollY) == 0) {
        background.draw(canvas);
    } else {
        canvas.translate(scrollX, scrollY);
        background.draw(canvas);
        canvas.translate(-scrollX, -scrollY);
    }
}

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

通過(guò)上面的源碼可以很容易的看出View背景的繪制過(guò)程:由于Drawable是沒(méi)有大小的概念的,所以在繪制背景之前先通過(guò)setBackgroundBounds方法設(shè)置Drawable的邊界,然后調(diào)用Drawable的draw方法繪制背景。
所以在自定義Drawable時(shí)要實(shí)現(xiàn)Drawable類中的draw方法,下面我實(shí)現(xiàn)了一個(gè)顯示圓形的自定義Drawable(CicleDrawable),并且CicleDrawable的大小會(huì)隨著View的變化而變化(有上面setBackgroundBounds方法的源碼得知)。CicleDrawable的實(shí)現(xiàn)代碼如下:

package com.cytmxk.test.drawable.custom;

import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;

/**
 * Created by chenyang on 16/8/13.
 */
public class CicleDrawable extends Drawable {

    private Paint paint;

    public CicleDrawable() {
        this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(0xFF00FF00);
    }

    @Override
    public void draw(Canvas canvas) {
        final Rect r = getBounds();
        canvas.drawCircle(r.exactCenterX(), r.exactCenterY(), Math.min(r.exactCenterX(), r.exactCenterY()), paint);
    }

    @Override
    public void setAlpha(int alpha) {
        paint.setAlpha(alpha);
        invalidateSelf();
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        paint.setColorFilter(colorFilter);
        invalidateSelf();
    }

    @Override
    public int getOpacity() {
        // not sure, so be safe
        return PixelFormat.TRANSLUCENT;
    }
}

上面的代碼我是參考ShapeDrawable類的源碼,所以說(shuō)源碼是一個(gè)很好的學(xué)習(xí)資料,有些技術(shù)細(xì)節(jié)我們不清楚,我們就可以查看源碼中類似功能的實(shí)現(xiàn),這樣就會(huì)有針對(duì)性的解決一些問(wèn)題。

Color State List Resource

Color State List Resource是一個(gè)管理著一個(gè)color集合的ColorStateList對(duì)象,每一個(gè)color對(duì)應(yīng)著一個(gè)狀態(tài),因此當(dāng)你將其作為color值供View 對(duì)象使用時(shí),Android系統(tǒng)會(huì)根據(jù)View對(duì)象的當(dāng)前狀態(tài)來(lái)選擇合適的color。例如,一個(gè)按鈕控件可以存在于幾種不同的狀態(tài)(pressed, focused, or niether),使用Color State List Resource可以為這些狀態(tài)提供不同的顏色。

file location:
    res/color/filename.xml
    The filename will be used as the resource ID.
compiled resource datatype:
    Resource pointer to a ColorStateList.
resource reference:
    In Java: R.color.filename
    In XML: @[package:]color/filename 

語(yǔ)法:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:color="hex_color"
        android:state_pressed=["true" | "false"]
        android:state_focused=["true" | "false"]
        android:state_selected=["true" | "false"]
        android:state_checkable=["true" | "false"]
        android:state_checked=["true" | "false"]
        android:state_enabled=["true" | "false"]
        android:state_window_focused=["true" | "false"] />
</selector>

1 <selector>節(jié)點(diǎn)元素:
Required 該節(jié)點(diǎn)元素一定被作為根元素。包含一個(gè)或多個(gè)<item>元素。

2 <item>節(jié)點(diǎn)元素:
通過(guò)屬性來(lái)描述在某個(gè)狀態(tài)下使用某個(gè)color值。必須是<selector>節(jié)點(diǎn)的子節(jié)點(diǎn)。
<item>節(jié)點(diǎn)元素的常用屬性:

android:color:
16進(jìn)制的color值. Required. 16進(jìn)制的color值是由一個(gè)RGB值和可選的透明值組成。16進(jìn)制的color值必須以字符'#'開(kāi)始并且是下面格式的一種:
#RGB
#ARGB
#RRGGBB
#AARRGGBB

android:state_pressed
Boolean. 如果屬性值為true,則使用該Color State List的組件被按下時(shí)該item被使用;如果屬性值為false,則使用該Color State List的組件沒(méi)有被按下時(shí)該item被使用;

android:state_focused
Boolean. 如果屬性值為true,則使用該Color State List的組件獲取input focus時(shí)該item被使用;如果屬性值為false,則使用該Color State List的組件沒(méi)有獲取input focus時(shí)該item被使用;

android:state_selected
Boolean. 如果屬性值為true,則使用該Color State List的組件被選擇時(shí)該item被使用;如果屬性值為false,則使用該Color State List的組件沒(méi)有被選擇時(shí)該item被使用。例如當(dāng)在幾個(gè)tab間切換時(shí),被選中的tab和沒(méi)有被選中的tab的背景顏色設(shè)置為不同的
github上的PagerSlidingTabStrip應(yīng)該利用到該屬性。

android:state_checkable
Boolean. "true" if this item should be used when the object is checkable; "false" if this item should be used when the object is not checkable. (Only useful if the object can transition between a checkable and non-checkable widget.)

android:state_checked
Boolean. 如果屬性值為true,則使用該Color State List的組件被checked時(shí)該item被使用;如果屬性值為false,則使用該Color State List的組件沒(méi)有被checked時(shí)該item被使用。

android:state_activated
Boolean. "true" if this item should be used when the object is activated as the persistent selection (such as to "highlight" the previously selected list item in a persistent navigation view); "false" if it should be used when the object is not activated.
Introduced in API level 11

注意:Android系統(tǒng)會(huì)從上到下進(jìn)行查找,直到查找到第一條匹配的item,并且如果Color State List Resource中的某一項(xiàng)沒(méi)有設(shè)置狀態(tài)屬性,則該項(xiàng)會(huì)被應(yīng)用每一次,因此默認(rèn)Item(沒(méi)有設(shè)置狀態(tài)屬性)應(yīng)該放到最后一項(xiàng)。
舉例如下:

XML file saved at res/color/button_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
          android:color="#ffff0000"/> <!-- pressed -->
    <item android:state_focused="true"
          android:color="#ff0000ff"/> <!-- focused -->
    <item android:color="#ff000000"/> <!-- default -->
</selector>
This layout XML will apply the color list to a View:
<Button
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/button_text"
    android:textColor="@color/button_text" />
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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