概述
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和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" />