Android 的 Drawable

Drawable 表示的是一种可以在 Canvas 上进行绘制的抽象概念。使用简单,比自定义 View 的成本低;非图片类型的 Drawable 占用空间小,有助于减小 APK 体积。

Drawable 使用范围单一,一个是作为 ImageView 中的图像来显示,另外一个就是作为 View 的背景。

Drawable 简介

在 Android 设计中,Drawable 是一个抽象类,它是所有 Drawable 的基类。

Drawable 的分类

1. BitmapDrawable
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:antialias="true" // 是否开启抗锯齿
android:dither="true" // 是否开启抖动效果,开启后让高质量图片在低质量屏幕保持较好的显示效果
android:filter="true" // 是否开启过滤效果
android:gravity="top" // 当图片小于容器尺寸时,可以对图片进行定位
android:mipMap="false" // 纹理映射,一般为 false
android:src="@drawable/image" // 图片资源 id
android:tileMode="disabled" // 平铺模式,选项有 disabledclamprepeatmirror >
</bitmap>

此外,也可以用 <nine-patch> 标签来表示一张 .9 格式的图片。

2. ShapeDrawable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" // 表示图形的形状:rectangle 矩形、oval 圆形、line 横线和 ring 圆环 >

<corners // 表示四个角的角度,只适用于 rectangle 矩形
android:bottomLeftRadius="0dp"
android:bottomRightRadius="15dp"
android:topLeftRadius="10dp"
android:topRightRadius="15dp"
android:radius="16dp" // 同时为四个角设定,优先级较低 />

<gradient // 表示渐变色,与 solid 标签互相排斥
android:angle="0" // 渐变的角度,默认为 0 ,值必须为 45 的倍数,0 表示从左到右,90 表示从下到上
android:centerColor="#00ff00" // 渐变的中间色
android:centerX="0.5" // 渐变的中心点横坐标
android:centerY="0.5" // 渐变的中心点纵坐标
android:endColor="#0000ff" // 渐变的结束色
android:startColor="#ff0000" // 渐变的起始色
android:gradientRadius="20dp" // 渐变半径,仅当 type = radial 时有效
android:type="linear" // 渐变类别,linear 线性渐变、radial 径向渐变、sweep 扫描线渐变/>

<solid android:color="#ff0000" // 纯色填充 与 gradient 标签互相排斥 />

<stroke // 表示描边
android:width="2dp" // 描边的宽度
android:color="#00ff00" // 描边的颜色
android:dashGap="2dp" // 组成虚线的线段宽度
android:dashWidth="10dp" // 组成虚线的线段之间的间隔 />

<padding android:left="20dp" // 表示包含这个 shape 的空白/>

<size // 表示 shape 的大小,但作为 View 的背景时还是会被拉伸或缩小
android:width="30dp"
android:height="30dp"/>
</shape>
3. LayerDrawable

表示一种层次化的 Drawable 集合,下面的 item 会覆盖上面的 item,通过合理的分层,可以实现一些特殊的叠加效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

<item>
<shape android:shape="rectangle" >
<solid android:color="#0ac39e" />
</shape>
</item>

<item android:bottom="6dp" // 相对于 View 的上下左右偏移量>
<shape android:shape="rectangle" >
<solid android:color="#ffffff" />
</shape>
</item>

</layer-list>

4. StateListDrawable

表示的是一种 Drawable 集合,每个 Drawable 都对应着 View 的一种状态,系统会根据 View 的状态选择合适的 Drawable。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 默认时的背景图片-->
<item android:drawable="@drawable/pic1" />
<!-- 没有焦点时的背景图片 -->
<item android:state_window_focused="false" android:drawable="@drawable/pic1" />
<!-- 非触摸模式下获得焦点并单击时的背景图片 -->
<item android:state_focused="true" android:state_pressed="true" android:drawable= "@drawable/pic2" />
<!-- 触摸模式下单击时的背景图片-->
<item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/pic3" />
<!--选中时的图片背景-->
<item android:state_selected="true" android:drawable="@drawable/pic4" />
<!--获得焦点时的图片背景-->
<item android:state_focused="true" android:drawable="@drawable/pic5" />
</selector>

5. LevelListDrawable

表示的是一种 Drawable 集合,每个 Drawable 都有一个等级(level)的概念,根据不同等级,会切换到对应的 Drawable。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8" ?>
<level-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/on"
android:maxLevel="10"
android:minLevel="5"/>

<item
android:drawable="@drawable/off"
android:maxLevel="5"
android:minLevel="0"/>

</level-list>

通过 Drawable 的 setLevel 或 ImageView 的 setImageLevel 来切换 Drawable。Level 的有效范围是 0~10000。

6. TransitionDrawable

用于实现两个 Drawable 之间的淡入淡出效果。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android" >

<item android:drawable="@drawable/drawable1"/>
<item android:drawable="@drawable/drawable2"/>

</transition>

通过 startTransitionreverseTransition 方法来实现效果。

1
2
TransitionDrawable drawable = (TransitionDrawable)textview.getBackground();
drawable.startTransition(1000);

7. InsetDrawable

可以将其他 Drawable 内嵌到自己当中,并可以在四周留出一定间距。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="15dp"
android:insetLeft="15dp"
android:insetRight="15dp"
android:insetTop="15dp" >

<shape android:shape="rectangle" >
<solid android:color="#ff0000" />
</shape>
</inset>

8. ScaleDrawable

它可以根据自己的等级(level)将指定的 Drawable 缩放到一定的比例。

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/image1" // 目标 drawable
android:scaleHeight="70%"
android:scaleWidth="70%"
android:scaleGravity="center" />

1
2
ScaleDrawable drawable = (ScaleDrawable)textview.getBackground();
drawable.setlevel(1);
9. ClipDrawable

它可以根据自己的等级(level)来裁切另一个 Drawable。

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical" // 表示裁切方向,只有水平和竖直两个方向
android:drawable="@drawable/image1"
android:gravity="bottom" // 与 clipOrientation 一起使用才有效果 />

1
2
ClipDrawable drawable = (ClipDrawable)textview.getBackground();
drawable.setlevel(5000);

等级越大,裁剪的区域越小,因此等级 10000 表示不裁切,而等级 0 表示裁切全部区域,图片将不可见。

自定义 Drawable

通常没必要去自定义 Drawable,因为无法在 XML 中去使用。下面是一个例子来实现简单的自定义 Drawable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class CustomDrawable extends Drawable {
private Paint mPaint;

public CustomDrawable(int color) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(color);
}

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

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

}

@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
invalidateSelf();
}

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

}

本章节配套源码戳我