Android Drawable 详解
@(Technical)[Android, Drawable, StateListDrawable, LayerDrawable, AnimationDrawable]
1、Drawable 简介
Drawable——可简单理解为可绘制物,表示一些可以绘制在 Canvas 上的对象。在日常的工作开发中,我们为 UI 配置背景、图片、动画等等界面效果的时候,需要和众多的 Drawable 打交道。每种 Drawable 的适用范围不同,我们有必要了解每种 Drawable 的特点以及使用方式,才能在工作中得心应手,少走弯路。
具体的配置、使用方法以及最终的界面效果大家可以在本文的附件里面看到。Drawable 在 Android 中的继承关系如下,其中,红框标注的几种 Drawable 是我们在开发中比较常用的一些:
Drawable 中比较重要的方法有以下几种:
Drawable
|- createFromPath
|- createFromResourceStream
|- createFromStream
|- createFromXml
|
|- inflate : 从XML中解析属性,子类需重写
|- setAlpha : 设置绘制时的透明度
|- setBounds : 设置Canvas为Drawable提供的绘制区域
|- setLevel : 控制Drawable的Level值,这个值在ClipDrawable、RotateDrawable、ScaleDrawable、AnimationDrawable等Drawable中有重要作用;区间为[0, 10000]
|- draw(Canvas) : 绘制到Canvas上,子类必须重写
其中,比较重要的方法是inflate
和draw
。inflate 方法用于从 XML 中读取 Drawable 的配置,draw 方法则实现了把一个 Drawable 确切的绘制到一个 Canvas 上面——draw 方法为一个abstract
抽象方法,子类必须进行重写。inflate 方法在Drawable.createFromXmlInner
中被调用:
我们可以看出,在从 XML 中创建一个 Drawable 时,步骤如下:
- 先根据 XML 节点名称来决定创造什么类型的 Drawable;然后 new 出相应的 Drawable;
- 再为该 Drawable 调用 inflate 方法,让其把配置加载起来——因为每种 Drawable 会重写 inflate 方法,所以,可以正确加载到各项配置及属性。XML 的配置我们稍后再讲。
setAlpha
方法用于设置一个 Drawable 的透明度,setBounds
用来指定当执行绘制时,在 Canvas 上的位置和区域。比如我们自定义一个 View,在其onDraw
中绘制一个BitmapDrawable
,我们设置了 BitmapDrawable 的 Alpha 和 Bounds,代码如下:
Drawable baseDrawable = getResources().getDrawable(R.drawable.base);
baseDrawable.setAlpha(100);
baseDrawable.setBounds(10, 20, 500, 300);
imageContent.setDrawable(baseDrawable);
绘制后的表现如下:
上图中,第一个区域是正常绘制的,第二个我们为 Drawable 设置了Alpha和Bounds,可以看出,右边深蓝色的纯色部分为整个 Canvas 的大小,设置了 100 的 Alpha 透明度后,图片把后面深蓝色的颜色也给透过来了,并且 Bounds 决定了 Canvas 上绘制该 Drawable 的区域大小和位置。
2、ColorDrawable
接下来我们逐一介绍 Drawable,着重介绍几种常用的 Drawable。由于在开发中这些 Drawable 大多在 XML 中进行配置,所以我们结合 XML 的配置类介绍。先从ColorDrawable
开始,这个应该是最简单的一种 Drawable 了,它用一个颜色值来表示:
color
|- color="#xxxxxx | @color/color_value"
|
比如我们的一个 ColorDrawable 的 XML 配置如下,以<color>
作为根节点:
<color
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#0000ff"/>
使用的时候和其他 Drawable 的使用方法类似,可以通过Resource.getDrawable
来获取,或者在 XML 里面配置:
<RelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/blue_drawable"/>
这个 View 的界面表现如你所想,是一坨蓝色:
Java代码实现:
Resources res = getResources();
ColorDrawable colorDrawable = new ColorDrawable();
colorDrawable.setColor(res.getColor(R.color.skin_black_item));
3、BitmapDrawable
BitmapDrawable
以<bitmap>
作为根节点:
bitmap
|- src="@drawable/res_id"
|- antialias="[true | false]"
|- dither="[true | false]"
|- filter="[true | false]"
|- tileMode="[disabled | clamp | repeat | mirror]"
|- gravity="[top | bottom | left | right | center_vertical |
| fill_vertical | center_horizontal | fill_horizontal |
| center | fill | clip_vertical | clip_horizontal]"
|
这个比较复杂一点了,我们逐一介绍各个属性:
- src:表示该 BitmapDrawable 引用的位图,该图片为 png、jpg 或者 gif;
- antialias:表示是否开启
抗锯齿
; - dither:表示当位图和屏幕的像素配置不同时,是否允许
抖动
。比如一张位图的像素为 ARGB_8888 32 位色,而屏幕像素为 RGB_565; - filter:是否允许为位图进行滤波以获取平滑的缩放效果;
- gravity:定义位图的
gravity
,当位图小于容器时,该属性指定了位图在容器中的停靠位置
和绘制方式
。 - tileMode:表示当位图小于容器时,执行
“平铺”
模式,并且指定铺砖的方法。该属性覆盖 gravity 属性——当指定了该属性后,gravity 属性即使设置了,也将不起作用。
其中,gravity
和tileMode
这两个属性比较有意思,我们着重来进行介绍。gravity 的默认值为fill
——亦即在水平和垂直方向均进行缩放,使得图片可以填充到整个 View 里面。
比如我们有一张如下的图片:
为了比较好的展现clamp 钳位模式
,注意这张图,我们在右边缘和下边缘用了黑白交替的边线。我们的 XML 配置极其简单,以<bitmap>
作为根节点:
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/car"
android:tileMode="repeat"/>
当这个 BitmapDrawable 放入一个比它大的容器中时,tileMode 就起作用了:
repeat
模式:将重复贴该图,直到填充完容器:
clamp
模式:钳位模式,将沿用下边、右边边缘的像素值分水平、垂直两个方向扩展填充剩余位置:
mirror
模式:镜像模式,将按水平、垂直镜像重复来填充剩余位置:
disabled
:禁用任何填充方法,将使用整个位图进行缩放填充。
我们接着来看 gravity 属性,该属性也比较容易理解:
top
:在顶部水平中心绘制;其他类如 left、right、bottom 和 top 类似;
当然,我们可以使用“|”来组合,达到特殊的效果,比如当 gravity 为bottom|right
时,表现如下:
center_horizontal
、center_vertical
将在水平、垂直两个方向上居中。当单独使用 top/left/right/bottom 四个值时,默认带了这两个中的值:比如 top == top|center_horizontal;fill_horizontal
、fill_vertical
将在水平、垂直两个方向上进行缩放填充,默认也是带了center_horizontal
或者center_vertical
这两个值的;
下面是“fill_vertical”
的表现: