一、Drawable简介
Drawable表示的是一种可以在Canvas上进行绘制的抽象概念。优点:使用简单,比自定义View成本低很多;非图片类型的Drawable占用空间较小。
可以通过getIntrinsicWidth/getIntrinsicHeight
这两个方法获取内部宽高。但并不是所有Drawable都有宽高:图片Drawable的内部宽/高就是图片的宽/高,但是颜色形成的Drawable并没有宽/高的概念。注意,Drawable的内部宽/高不等同于它的大小,一般来说,Drawable没有大小的概念,因其通常被用作View背景,Drawable会被拉伸至View的同等大小。
二、Drawable的分类
1、BitmapDrawable
表示的就是一张图片,可以直接引用原始图片即可,也可以通过XML描述它,从而设置更多效果。
<?xml version="1.0" encoding="utf-8"?>
<bitmap/nine-patch
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:antialias=["true" | "false"]
android:dither=["true" | "false"]
android:filter=["true" | "false"]
android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
"fill_vertical" | "center_horizontal" | "fill_horizontal" |
"center" | "fill" | "clip_vertical" | "clip_horizontal"]
android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] />
android:src
图片资源idandroid:antialias
是否开启图片抗锯齿功能。开启后会让图片变得平滑,同时也会一定程度上降低图片的清晰度,建议开启;android:dither
是否开启抖动效果。当图片的像素配置和手机屏幕像素配置不一致时,开启这个选项可以让高质量的图片在低质量的屏幕上还能保持较好的显示效果,建议开启。android:filter
是否开启过滤效果。当图片尺寸被拉伸或压缩时,开启过滤效果可以保持较好的显示效果,建议开启;-
android:gravity
当图片小于容器的尺寸时,设置此选项可以对图片进行定位。
android:tileMode
平铺模式,有四种选项[“disabled” | “clamp” | “repeat” | “mirror”]
。当开启平铺模式后,gravity属性会被忽略。repeat是指水平和竖直方向上的平铺效果;mirror是指在水平和竖直方向上的镜面投影效果;clamp是指图片四周的像素会扩展到周围区域,这个比较特别。
NinePatchDrawable
表示一张.9格式的图片,它和BitmapDrawable都表示一张图片。用XML描述的方式也和BitmapDrawable一样。在bitmap标签中也可以使用.9图。
2、ShapeDrawable
可以理解为通过颜色来构造的图形,可以是纯色或渐变的图形。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="10dp"
android:bottomRightRadius="10dp"
android:radius="5dp"
android:topLeftRadius="10dp"
android:topRightRadius="10dp" />
<gradient
android:angle="0"
android:centerColor="#cccccc"
android:centerX="100"
android:centerY="20"
android:endColor="#abcdef"
android:gradientRadius="100dp"
android:startColor="#000000"
android:type="linear"
android:useLevel="false" />
<solid android:color="#cccccc" />
<stroke
android:width="1dp"
android:color="#cccccc"
android:dashGap="2dp"
android:dashWidth="50dp" />
-
android:shape
表示图片的形状,选项:rectangle(矩形)、oval(椭圆)、line(横线)、ring(圆环)。默认值是矩形,另外line和ring这两个选项必须通过<stroke>标签来指定宽度和颜色,否则看不到效果。 -
<corners>
表示shape的四个角的角度(圆角程度)。只适用于矩形shape。 -
<gradient>
与<solid>标签相互排斥的,其中solid表示纯色填充,而gradient表示渐变效果;
gradient有如下几个属性:
android:angle
—— 渐变的角度,默认为0,其值必须是45的倍数,0表示从左往右,90表示从下到上。
android:centerX
渐变的中心点的横坐标
android:centerY
渐变的中心点的纵坐标;
android:startColor
渐变的起始色
android:centerColor
渐变的中间色
android:endColor
渐变的结束色
android:gradientRadius
渐变半径,仅当android:type=”radial”时有效。
android:type
渐变的类型,有linear(线性渐变)、radial(镜像渐变)、swepp(扫描线渐变)三种,默认是线性渐变。 -
<solid>
表示纯色填充,通过android:color即可指定shape中填充的颜色。 -
<stroke>
android:width
描边的宽度
android:color
描边的颜色
android:dashWidth
组成虚线的线段的宽度
android:dashGap
组成虚线之间的间距。dashWidth和dashGap有任何一个为0,虚线效果都不能生效。 -
<padding>
表示空白,但不是shape的空白,而是包含它的View的空白。 -
<size>
shape的大小,有两个属性:android:width
和android:height
,分别表示shape的宽高。通过标签指定宽高后,ShapeDrawable就有固定宽/高了。但是作为view的背景来说,shape还是会被拉伸或者缩小为view的大小。
3、LayerDrawable
一种层次化的Drawable集合,通过将不同的Drawable放置在不同层后达到一种叠加效果。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/res_haimei1"
android:bottom="10dp"
android:drawable="@mipmap/haimei1" //可以引用已有的Drawable资源
android:left="10dp"//表示的是相对于View的上下左右偏移量
android:right="10dp"
android:top="10dp" />
<item
android:id="@+id/res_icon"
android:width="30dp"
android:height="30dp"
android:drawable="@mipmap/ic_launcher"
android:gravity="center" />
</layer-list>
一个layout-list中可以包含多个item,每个item表示一个Drawable。可以通过drawable属性直接引用已有的Drawable资源,也可以在item自定义Drawable。
4、StateListDrawable
对应<selector>标签。它也表示Drawable集合,每个Drawable对应View的一种状态,这样系统就会根据View的状态来选择合适的Drawable。主要用于设置可点击View的背景。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="false"
android:dither="true"
android:variablePadding="false">
<item android:drawable="@mipmap/ic_launcher" android:state_pressed="true" />
<item android:drawable="@mipmap/haimei1" android:state_pressed="false" />
</selector>
-
android:constantSize
StateListDrawable的固有大小是否不随着其状态的变化而变化,因为不同的Drawable有不同的固有大小。true表示固有大小保持不变,这时它的固有大小是内部所有Drawable的固有大小的最大值。默认值为false。 - ``android:dither`
是否开启抖动效果,默认true -
android:variablePadding
StateListDrawable的padding是否随着状态变化而变化。true表示变化,false表示padding是内部所有Drawable的padding的最大值。默认为false。
view的常见状态
android:state_pressed 按下状态,Button按下之后没有松开
android:state_focused View获取了焦点
android:state_selected 用户选择了View,如RadioButton
android:state_checked 用户选中了View,适用于CheckBox
android:state_enable View处于可用状态
系统会从上到下的顺序查找,直到找到第一条匹配的item。默认的item一般放在最后并且不添加任何状态,这样当系统在之前的item无法选择的时候,就会匹配默认的item,因为item的默认状态不附带任何状态,所以它可以适配任何状态。
5、LevelListDrawable
对应<level-list>标签。同样表示Drawable集合,集合中的每个Drawable都会有一个等级的概念,根据等级不同来切换对于的Drawable。当它作为View的背景时,可以通过Drawable的setLevel方法来设置不同的等级从而切换具体的Drawable。level的值从0-10000,默认为0。
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:maxLevel="0" android:drawable="@drawable/ic_playmethod_normal" />
<item android:maxLevel="1" android:drawable="@drawable/ic_playmethod_repeat_list" />
<item android:maxLevel="2" android:drawable="@drawable/ic_playmethod_repeat_one" />
<item android:maxLevel="3" android:drawable="@drawable/ic_playmethod_random" />
</level-list>
6、TransitionDrawable
对应<transition>标签。用于实现两个Drawable之间淡入淡出的效果。
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/haimei2" />
<item android:drawable="@mipmap/haimei3" />
</transition>
TransitionDrawable drawable = (TransitionDrawable) imageView.getBackground();
drawable.startTransition(1000);
startTransition
和reverseTransition
方法实现淡入淡出的效果以及它的逆过程。
7、InsetDrawable
对应于<inset>标签。它可以将其他Drawable内嵌到自己当中,并可以在四周留下一定的间距。当一个View希望自己的背景比自己的实际区域小的时候,可以采用InsetDrawable来实现。通过LayerDrawable也可以实现。
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@mipmap/haimei1"
android:insetBottom="10dp"//内凹的大小
android:insetLeft="10dp"
android:insetRight="10dp"
android:insetTop="10dp">
<shape android:shape="rectangle">
<solid android:color="#abcdef" />
</shape>
</inset>
8、ScaleDrawable
对应于<scale>标签,可以根据自己的level将指定的drawable缩放到一定比例。
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@color/blue"
android:level="1"
android:scaleGravity="center"
android:scaleHeight="20%"
android:scaleWidth="20%" />
android:scaleGravity属性相当于gravity属性。android:scaleHeight/scaleWidth 表示Drawable的缩放比例。
缩放公式: w -= (int) (w * (10000 - level) * mState.mScaleWidth / 10000)
可见,level越大,Drawable看起来越大;scaleHeight/scaleWidth越大,Drawable看起来越小。注意的是,level设置为0时,Drawable不可见。level不应超过10000。
9、ClipDrawable
对应于<clip>标签,他可以根据自己当前的等级(level)来裁剪一个Drawable,裁剪方向可以通过Android:clipOrientation和android:gravity两个属性共同控制。
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical\horizontal"
android:drawable="@drawable/bitmapdrawable"
android:gravity="bottom|top|left|right|center|fill|center_vertical
|center_horizontal|fill_vertical|fill_horizontal
|clip_vertical|clip_horizontal" />
clipOrientation表示裁剪方向。gravity需要和clipOrientation一起才能发挥作用。如下所示:
使用步骤:
- 定义ClipDrawable
- 布局文件引用
- 代码控制level
ImageView imageClip = (ImageView) findViewById(R.id.image_clip);
ClipDrawable drawable = (ClipDrawable) imageClip.getDrawable();
drawable.setLevel(5000);
level=0的时候,表示完全裁剪,level=10000的时候表示完全不裁剪,level=5000的时候表示裁剪了一半。即等级越大,裁剪的区域越小。
三、自定义Drawable
Drawable的作用很单一:一个是最为ImageView中的图像来显示,另一个是作为View的背景(大部分情况)。
在第5章中,我们分析了View的工作原理,系统会调用Drawable的draw方法绘制view的背景。所以我们可以通过重写Drawable的draw方法来自定义Drawable。但是,通常我们没必要自定义Drawable,因为自定义Drawable无法在XML中使用。只有在特殊情况下可以使用自定义Drawable。