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的类型,可用的有效值如下:

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



作者:ForeverCy
链接:https://www.jianshu.com/p/e1d08b757e7e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值