Drawable Resources与Color State List Resource

概述

Android把任何可绘制在屏幕上的图形图像都称为drawable 资源,你可以通过类似getDrawable(int)的API来获取drawable资源,你也可以通过类似android:drawable 、 android:icon的属性将drawable 资源应用到其他的XML资源中。Drawable表示的是一种可以在Canvas上进行绘制的抽象的概念、它的种类有很多,它们都表示一种图像的概念,通过图片或者颜色可以构造出各式各样的图像的效果,也可以通过继承Drawable类自定义Drawable来构建图像的效果。
我们经常会接触到的drawable: state list drawable、shape drawable、 layer list drawable、ripple drawable、inset drawable以及nine patch drawable。前5个drawable通常定义在XML布局文件中,因此我们统一将它们归属为XML drawable类别。这6中常用的drawable与Drawable类之间的关系如下图所示:


Drawable类继承关系图

下面我会讲解上面提到的六种常用的Drawable和Color State List Resource,最后再讲解一下自定义Drawable。

预备知识

1 关于Image Dithering(图像抖动)的介绍
Image Dithering(图像抖动) 是 Dithering(抖动)在数字图片处理中的一种应用。一般情况下是指一种使数字图片在降低色深的情况下,呈现最佳展现的技术。比如将24bit色深的图像使用8bit色深来展现。
例图:


原图

原图降低色深,没有加抖动处理

原图降低色深,加抖动处理

图像抖动技术常用的算法是 Floyd–Steinberg dithering.
编程应用: 在Android应用开发中,使用 android:dither 属性来设置图像是否应用抖动处理。

几种常用的XML drawable

1 shape drawable
使用XML文件来定义几何图形,可以理解为通过颜色来构造的几何图形,它既可以是纯色的几何图形,也可以是具有渐变效果的几何图形。对应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

语法:

<?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>节点元素
该节点元素一定被用作根元素。
<shape>节点元素的常用属性:
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这两个类型必须要通过stroke节点元素来指定线的宽度和颜色等信息,否者无法达到预期的显示效果。
2> 只有当android:shape="ring"时,以下的属性才被使用:

android:innerRadius
Dimension 内环的半径,可直接使用尺寸值或者引用尺寸资源。

android:innerRadiusRatio
Float 内环半径与环宽度的比例。例如,如果android:innerRadiusRatio="5",则内环半径等于drawable宽度的5分之1,这个值会被android:innerRadius属性值重写。默认值为3。

android:thickness
Dimension 环的厚度,可直接使用尺寸值或者引用尺寸资源。

android:thicknessRatio
Float 环的厚度与环宽度的比例。例如,如果android:thicknessRatio="2",则环的厚度等于drawable的宽度除以2。这个值会被android:innerRadius属性值重写。默认值是9。

android:useLevel
Boolean 一般都应该使用false,否者有可能无法达到预期的显示效果,除非它被当做LevelListDrawable来使用。

1.2 <corners>节点元素
用来为shape drawable创建圆角,仅适用于shape drawable是矩形时,即只有<shape>节点中android:shape属性为rectangle时。
<corners>节点元素的常用属性:

android:radius
Dimension 所有圆角的半径,可直接使用尺寸值或者引用尺寸资源。每一个圆角的半径值会被如下的属性值重写。

android:topLeftRadius
Dimension 左上圆角的半径,可直接使用尺寸值或者引用尺寸资源。

android:topRightRadius
Dimension 右上圆角的半径,可直接使用尺寸值或者引用尺寸资源。

android:bottomLeftRadius
Dimension 左下圆角的半径,可直接使用尺寸值或者引用尺寸资源。

android:bottomRightRadius
Dimension 右下圆角的半径,可直接使用尺寸值或者引用尺寸资源。

注意:圆角的半径必须大于1,否则没有圆角。如果你想要指定某个角不是圆角,一个解决方法是使用android:radius属性去给圆角半径设置一个大于1的默认值,然后重写你不想是圆角的对应属性值为0dp。

1.3 <gradient>节点元素
为shape drawable指定一种渐变颜色。它与<solid>节点元素是互相排斥的,其中solid表示纯色填充,而gradient则表示渐变效果。
<gradient>节点元素的常用属性:

android:angle
Integer 颜色渐变的方向,0 is left to right, 90 is bottom to top.必须是45的倍数. Default is 0.

android:type
Keyword 渐变图案的类型,可用的有效值如下:
android:startColor
Color颜色渐变的开始颜色,为十六进制颜色值值或者引用Color资源。

android:endColor
Color颜色渐变的结束颜色,为十六进制颜色值值或者引用Color资源。

android:centerColor
Color颜色渐变的中间颜色,为十六进制颜色值或者引用Color资源。

android:centerX
Float (0 - 1.0) 相对X的渐变位置,默认值为0.5。在type为linear时而且只有centerColor被设置时该属性才会起作用。

android:centerY
Float (0 - 1.0) 相对Y的渐变位置,默认值为0.5。在type为linear时而且只有centerColor被设置时该属性才会起作用。

android:gradientRadius
Float 渐变的半径,单位应该是像素点. 仅当android:type="radial"时有效.

android:useLevel
Boolean 一般为false,当Drawable作为LevelListDrawable使用时为true.

举例说明:
例一:
颜色从透明到不透明渐变。



运行结果如下:



例二:

运行结果如下:

例三:



运行结果如下:

例四:

运行结果如下:

例五:

运行结果如下:

1.4 <solid>节点元素
这个节点元素表示纯色填充,通过android:color即可指定shape drawable中填充的颜色。

1.5 <stroke>节点元素
描边。
<stroke>节点元素的常用属性:

android:width="2dp"   描边的宽度。
android:color   描边的颜色。

我们还可以把描边弄成虚线的形式,设置方式为:
android:dashWidth="5dp"   表示'-'这样一个横线的宽度.
android:dashGap="3dp"     表示'-'之间隔开的距离。

1.6 <padding>节点元素
用来设置包含该shape drawable的View的padding
<padding>节点元素的常用属性:

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

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

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

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

1.7 <size>节点元素
shape drawable的固有大小。
<size>节点元素的常用属性:

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

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

上面的两个属性表示的是shape drawable的固有宽高,但是一般来说它并不是shape drawable最终显示的宽高,这个有点抽象,但是我们要明白,对于shape drawable来说它并没有宽高的概念,作为View的背景shape drawable会自适应View的宽高。我们知道Drawable的两个方法getIntrisicWidth和getIntrinsicHeight表示的是Drawable的固有宽高,对于有些Drawable比如BitmapDrawable,它的固有宽高就是图片的尺寸;而对于shape drawable来说,默认情况下他是没有固有宽高的概念的,这个时候方法getIntrisicWidthhe和getIntrinsicHeight会返回-1,但是如果通过<size>标签来指定宽高信息,那么这个时候shape drawable就有了所谓的固有宽高;因此,总结来说,<size>标签设置的宽高就是shape drawable的固有宽高,但是作为View的背景时,shape drawable还会被拉伸或者缩小为View的大小。

2 inset drawable
当一个View希望自己的背景比自己的实际区域小的时候,这非常有用。对应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 

语法:

<?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>节点元素:
<inset>节点元素的常用属性:

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是一个管理着一个Drawable对象数组的LayerDrawable类的对象,数组中的每一个Drawable对象按照下标的顺序进行绘制,因此数组中的最后一个Drawable对象被绘制在最上面。
每一个Drawable是通过一个<item>元素来表示,每一个<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

语法:

<?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>

一个layer list drawable中可以包含多个item,每个item表示一个Drawable。item的结构比较简单,比较常用的属性有android:top、android:right、android:bottom和android:left,他们分别表示Drawable相对于View的上下左右的偏移量,单位为像素。另外,我们可以通过android:drawable来引用一个已有的Drawable资源,当然也可以在item中自定义Drawable。

4 ripple drawable
该drawable是在api21(L)时被引进的,可以实现触摸反馈的效果(波浪的效果)。对应RippleDrawable类,继承LayerDrawable(即继承layer list drawable的所有特点)。

4.1 针对ImageView的background属性
下面三个例子是针对ImageView的background属性的:
对于三个例子的总结(针对background属性):
波浪最大区域是该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中没有item子节点或者包含有一个id被指定为@android:id/mask的item并且将该ripple drawable设置为View的background属性值时,波浪动画将会绘制在视图层级结构中第一个可见的该View的父视图之上,在这种情况下,波浪动画可能会越过View区域,波浪动画最大区域是该View区域的外接圆,但是不会超过第一个可见父视图的区域。

举例代码如下:

/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>

运行结果如下:


例二
在例一的基础上在ripple根节点上添加一个item项,相当于ripple drawable当前拥有一个Drawable图层并且当将该ripple drawable设置为View的background属性值时该Drawable图层可以限制波浪动画不会越过View区域,波浪动画将被绘制在该View区域上,在这种情况下,波浪动画不会越过View区域,波浪最大区域是该View区域的外接圆,但是波浪动画不会越过View区域。

举例代码如下:

/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>

运行结果如下:


例三
在例二中添加的item节点中添加android:id="@android:id/mask",相当于当将该ripple drawable设置为View的background属性值时该item节点定义的Drawable图层仅仅是用来限制波浪动画不会越过View区域。波浪动画将被绘制在该View区域上,在这种情况下,波浪动画不会越过View区域,波浪最大区域是该View区域的外接圆,但是波浪动画不会超过View区域。
举例代码如下:

/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>

运行结果如下:


4.2 针对ImageView的src属性的

下面三个例子是针对ImageView的src属性的:
对于三个例子的总结(针对src属性):
波浪最大区域是该View内容的外接圆

例一:
在ripple中没有item子节点或者包含有一个id被指定为@android:id/mask的item并且将该ripple drawable设置为View的src属性值时,波浪动画将会绘制在该View区域上,在这种情况下,波浪动画可能会越过View内容区域,波浪动画最大区域是该View内容区域的外接圆,但是不会超过View区域。
举例代码如下:

/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>

运行结果如下:


例二:
在例一的基础上在ripple根节点上添加一个item项,相当于ripple drawable当前拥有一个Drawable图层并且当将该ripple drawable设置为View的src属性值时该Drawable图层可以限制波浪动画不会越过View内容区域,波浪动画将被绘制在该View内容区域上,在这种情况下,波浪动画不会越过View内容区域,波浪最大区域是该View内容区域的外接圆,但是波浪动画不会越过View内容区域。
举例代码如下:

/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>

运行结果如下:


例三
在例二中添加的item节点中添加android:id="@android:id/mask",相当于当将该ripple drawable设置为View的src属性值时该item节点定义的Drawable图层仅仅是用来限制波浪动画不会越过View内容区域。波浪动画将被绘制在该View内容区域上,在这种情况下,波浪动画不会越过View内容区域,波浪最大区域是该View内容区域的外接圆,但是波浪动画不会超过View内容区域。
举例代码如下:

/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>

运行结果如下:


5 state list drawable
state list drawable是一个管理着一个Drawable对象集合的StateListDrawable类的对象。集合中的每一个Drawable对象对应View的一种状态,这样系统会根据View的状态来选择合适的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

语法:

<?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>节点元素:
Required 该节点元素一定被作为根元素。包含一个或多个<item>元素。
<selector>节点元素的常用属性:

android:constantSize
state list drawable的固有大小是否不随着View状态的改变而改变,因为View状态会导致
state list drawable切换到具体的Drawable,而不同的Drawable具有不同的固有大小。
true表示state list drawable的固有大小保持不变,这时state list drawable的固有大小是
内部所有Drawable的固有大小的最大值,false则会随着View状态的改变而改变。
此选项的默认值是false。

android:dither
是否开启抖动效果,开启此选项可以让图片在低质量的屏幕上仍然获得较好的显示效果。
此选项默认值为true。

android:variablePadding
state list drawable的padding表示是否随着View状态的改变而改变,true表示会随着View状态的改变而改变,false表示state list drawable的padding是内部所有Drawable的padding的最大值。此选项默认值为false,并且不建议开启此选项。

5.2 <item>节点元素:
通过属性来描述在某个状态下使用某个drawable。必须是<selector>节点的子节点。
<item>节点元素的常用属性:

android:drawable:
Drawable resource. Required.引用一个drawable resource.

android:state_pressed
Boolean. 如果属性值为true,则使用该state list drawable的组件被按下时该item被使用;如果属性值为false,则使用该state list drawable的组件没有被按下时该item被使用;

android:state_focused
Boolean. 如果属性值为true,则使用该state list drawable的组件获取input focus时该item被使用;如果属性值为false,则使用该state list drawable的组件没有获取input focus时该item被使用;

android:state_selected
Boolean. 如果属性值为true,则使用该state list drawable的组件被选择时该item被使用;如果属性值为false,则使用该state list drawable的组件没有被选择时该item被使用。例如当在几个tab间切换时,被选中的tab和没有被选中的tab的背景颜色设置为不同的
github上的PagerSlidingTabStrip应该利用到该属性。

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时该item被使用;如果属性值为false,则使用该state list drawable的组件没有被checked时该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系统会从上到下进行查找,直到查找到第一条匹配的item,并且如果state list drawable中的某一项没有设置状态属性,则该项会被应用每一次,因此默认Item(没有设置状态属性)应该放到最后一项。

nine patch drawable

9-patch图像是一种特殊格式的文件,因为Android知道图像的哪些部分可以拉伸缩放,哪些部分不可以。经适当处理后,可保证背景图的边角与工具创建的图像保持一致性。
为什么要叫做9-patch呢? 9-patch可将图像分成3× 3的网格,即由 9部分或9 patch组成的网格。网格角落的patch不会被缩放,边缘部分的4个patch只按一个维度缩放,而中间部分则同时按两个维度缩放,如下图所示:



9-patch图像和普通的png图像基本相同,但以下两点除外: 9-patch图像文件名是以.9.png结尾的,图像边缘具有一个像素宽度的边框,顶部以及左边框黑线用来标记图像的可伸缩区域,底部以及右边框黑线用于标记9-patch图像的可填充内容区域。 可填充内容区域是内容(通常是文字)绘制的地方。若不设置可填充内容区域,则默认与可拉伸区域保持一致。对应NinePatchDrawable类。可通过如下两种方式使用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 通过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 

语法:

<?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>节点元素:
< nine-patch>节点元素的常用属性:

android:src:
Drawable resource. Required.引用一个Nine-Patch File

android:dither
当位图不具有与屏幕相同的像素结构时,是否启用或禁用抖动效果(关于抖动效果的说明可以参考预备知识部分)。

举例如下:

<?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背景的(也是大多数情况下),另一种方式是作为ImageView的内容显示。在我的另一篇文章Android自定义View中再讲解View的绘制过程中提到过绘制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();
    }
}

通过上面的源码可以很容易的看出View背景的绘制过程:由于Drawable是没有大小的概念的,所以在绘制背景之前先通过setBackgroundBounds方法设置Drawable的边界,然后调用Drawable的draw方法绘制背景。
所以在自定义Drawable时要实现Drawable类中的draw方法,下面我实现了一个显示圆形的自定义Drawable(CicleDrawable),并且CicleDrawable的大小会随着View的变化而变化(有上面setBackgroundBounds方法的源码得知)。CicleDrawable的实现代码如下:

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类的源码,所以说源码是一个很好的学习资料,有些技术细节我们不清楚,我们就可以查看源码中类似功能的实现,这样就会有针对性的解决一些问题。

Color State List Resource

Color State List Resource是一个管理着一个color集合的ColorStateList对象,每一个color对应着一个状态,因此当你将其作为color值供View 对象使用时,Android系统会根据View对象的当前状态来选择合适的color。例如,一个按钮控件可以存在于几种不同的状态(pressed, focused, or niether),使用Color State List Resource可以为这些状态提供不同的颜色。

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 

语法:

<?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>节点元素:
Required 该节点元素一定被作为根元素。包含一个或多个<item>元素。

2 <item>节点元素:
通过属性来描述在某个状态下使用某个color值。必须是<selector>节点的子节点。
<item>节点元素的常用属性:

android:color:
16进制的color值. Required. 16进制的color值是由一个RGB值和可选的透明值组成。16进制的color值必须以字符'#'开始并且是下面格式的一种:
#RGB
#ARGB
#RRGGBB
#AARRGGBB

android:state_pressed
Boolean. 如果属性值为true,则使用该Color State List的组件被按下时该item被使用;如果属性值为false,则使用该Color State List的组件没有被按下时该item被使用;

android:state_focused
Boolean. 如果属性值为true,则使用该Color State List的组件获取input focus时该item被使用;如果属性值为false,则使用该Color State List的组件没有获取input focus时该item被使用;

android:state_selected
Boolean. 如果属性值为true,则使用该Color State List的组件被选择时该item被使用;如果属性值为false,则使用该Color State List的组件没有被选择时该item被使用。例如当在几个tab间切换时,被选中的tab和没有被选中的tab的背景颜色设置为不同的
github上的PagerSlidingTabStrip应该利用到该属性。

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时该item被使用;如果属性值为false,则使用该Color State List的组件没有被checked时该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系统会从上到下进行查找,直到查找到第一条匹配的item,并且如果Color State List Resource中的某一项没有设置状态属性,则该项会被应用每一次,因此默认Item(没有设置状态属性)应该放到最后一项。
举例如下:

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

推荐阅读更多精彩内容