Android中Drawable知识总结

本文是学习《Android开发艺术探索》中Drawable章节之后的一个总结。

一、常见的Drawable种类介绍

Drawable类 xml标签 描述
BitmapDrawable <bitmap/> 表示一张图片,与直接引用原始图片相比可以设置一些效果
ShapeDrawable <shape/> 通过颜色构造各种形状的图形,标签对应的实体类实际是GradientDrawable
LayerDrawable <layer-list/> 表示一种层次化的Drawable集合,可以将不同的Drawable放置在不同的层上达到叠加效果
StateListDrawable <selector/> 表示一种Drawable集合,集合中每个Drawable对应着View的一种状态,最常用于Button
LevelListDrawable <level-list/> 表示一种Drawable集合,集合中每个Drawable(item)都有一个等级(level)的概念。可以根据不同的等级切换对应的Drawable。每个Drawable(item)对应一个等级范围,可以通过Drawable的setLevel方法来切换,如果用作ImageView的前景,还可以通过ImageView的setImageLevel方法来切换Drawable。level范围0-10000。
TransitionDrawable <transition/> 用于实现两个Drawable之间的淡入淡出效果,通过该Drawable中的startTransition和reverseTransition方法实现淡入淡出和逆过程,两个方法接收一个时间参数。
InsetDrawable <inset/> 可以将其他Drawable内嵌到自己当中,并且可以在四周流出一定的距离。当一个View希望背景比自己实际区域小的时候,可以用这个Drawable。LayerDrawable可以实现相同的效果
ScaleDrawable <scale/> 可以根据自己的等级将指定的Drawable缩放到一定的比例。
ClipDrawable <clip/> 根据自己当前的等级来裁剪另一个Drawable,裁剪方向可以通过android:clipOrientation和android:gravity两个属性共同控制。



二、各种Drawable的xml属性详解

1.<bitmap/>

<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="图片资源id"
    android:antialias="true|false" 
    android:dither="true|false"
    android:filter="true|false"
    android:gravity="..."
    android:mipMap="true|false"
    android:tileMode="disabled|clamp|repeat|mirror" />

antialias抗锯齿,开启后会让图片变得平滑,同时也会在一定程度上降低图片的清晰度,但是降低的幅度低至可以忽略,所以应该开启;
dither抖动效果,当图片的像素配置与手机屏幕的像素配置不一致时,开启这个选项可以让高质量图片在低质量的屏幕上还能保持较好的显示效果,比如图片的色彩模式为ARGB8888,但是设备屏幕所支持的色彩模式为RGB555,这时候开启抖动选项可以让图片显示不会过于失真,在Android中创建Bitmap一般会选用ARGB8888这个模式,在这种色彩模式下一个像素所占的大小为4个字节,一个像素的位数总和越高,图像也就越逼真。根据分析,抖动效果应该开启;
filter过滤效果,当图片尺寸被拉伸或压缩时,过滤可以保持较好的显示效果,应该开启;
mipMap一种图像处理技术,不常用,默认false即可;
titleMode平铺模式。

点9图片对应NinePatchDrawable,xml标签是<nine-patch/>

2.<shape/>

<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="integer"
        android:centerY="integer"
        android:centerColor="color"
        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>

android:shape图形形状,四个选项:rectangle(矩形)、oval(椭圆)、line(横线)、ring(圆环)。默认是矩形,line和ring必须通过<stroke>标签来指定线的宽度和颜色等信息,否则无法达到预期的显示效果。
针对ring这个形状,有5个特殊属性:
android:innerRadius 内环半径,与android:innerRadiusRatio同时存在时,以android:innerRadius为准
android:thickness 圆环厚度,即外半径减去内半径的大小,与android:thicknessRatio同时存在时以android:thickness为准
android:innerRadiusRatio 内半径占整个Drawable的宽度比例,默认值9。如果是n,那么内半径=宽度/n
android:thicknessRatio 厚度占整个Drawable的宽度比例,默认值3。如果是n,那么厚度=宽度/n
android:useLevel 一般都应该使用false,否则有可能无法达到预期效果,除非被当作LevelListDrawable来使用

<corners>

表示四个角的角度,只适用于shape,这里的角度指的是圆角的程度,用px来表示,5个属性:
android:radius 四个角同时设定相同的角度,优先级较低,会被其他4个属性覆盖。
android:topLeftRadius 左上角
android:topRightRadius 右上角
android:bottomLeftRadius 左下角
android:bottomRightRadius 右下角

<gradient>

表示渐变色,与<solid>纯色标签互斥,属性如下:
android:angle 渐变的角度,影响渐变方向,默认为0,值必须是45的倍数,比如0表示从左到右,90表示从上到下
android:centerX 渐变中心点的横坐标
android:centerY 渐变中心点的纵坐标,渐变的中心点影响渐变的具体效果
android:startColor 渐变的起始色
android:centerColor 渐变的中间色
android:endColor 渐变的结束色
android:gradientRadius 渐变半径,仅当type=radial时有效
android:type 渐变类别,有三个值:linear(线性渐变)、radial(径向渐变)、sweep(扫描线渐变)。默认是线性渐变。
android:useLevel 一般为false,当作为StateListDrawable使用时为true

<solid>

纯色填充,属性android:color表示填充颜色

<stroke>

Shape的描边,属性如下:
android:width 描边的宽度,越大shape的边缘性看起来越粗
android:color 描边的颜色
android:dashWidth 组成虚线的线段的宽度
android:dashGap 组成虚线的线段之间的间隔
android:dashWidth和android:dashGap有一个为0,那么虚线效果不生效。

<padding>

表示包含它的View的空白,有上下左右四个属性。

<size>

shape的大小,但不是shape最终的大小因为shape一般会自适应View的宽高。

3.<layer-list/>

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable=""
        android:id=""
        android:top=""
        android:right=""
        android:bottom=""
        android:left="" />
</layer-list>

一个layer-list可以包含多个item,每个item表示一个Drawable。
android:top android:right android:bottom android:left设置上下左右的偏移量。
android:drawable 直接引用一个Drawable资源,也可以在item中自定义Drawable。
默认情况layer-list中的所有Drawable都会被缩放至View的大小

4.<selector/>

<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=""
        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>

android:constantSize属性表示StateListDrawable的固有大小是否不随状态的改变而改变,因为状态的改变会导致切换到具体的Drawable,而不同状态的Drawable可能大小不同。true表示固有大小保持不变。dither表示抖动效果。android:variablePadding表示StateListDrawable的padding是否随状态的改变而改变,不建议开启。

<item>

每个item都表示一种状态下的Drawable信息。常见状态如下:
android:state_pressed 按下状态
android:state_focused 获取焦点
android:state_selected 用户选择了View
android:state_checked 用户选中了View,一般用于CheckBox这类在选中和非选中状态之间切换的View
android:state_enabled View处于可用状态

系统会根据View的状态从selector中选择对应的item,按照从上到下的顺序查找,直至查找到第一个匹配的item。一般默认的item都应该放在selector的最后一条并且不附带任何状态,这样当上面的item都无法匹配View的当前状态时,就会选择默认的item,因为默认的item不附带状态,所以它可以匹配View的任何状态。

5.<level-list/>

<level-list
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable=""
        android:minLevel=""
        android:maxLevel="" />
</level-list>

示例代码

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@drawable/s1" android:minLevel="0" android:maxLevel="20"/>
    <item android:drawable="@drawable/s2" android:minLevel="21" android:maxLevel="40"/>
    <item android:drawable="@drawable/s3" android:minLevel="41" android:maxLevel="60"/>
    <item android:drawable="@drawable/s4" android:minLevel="61" android:maxLevel="100"/>
</level-list>
//id是level_list_img的ImageView用上面的level-list做背景
ImageView iv = (ImageView) findViewById(R.id.level_list_img); 
LevelListDrawable levelListDrawable = (LevelListDrawable) iv.getDrawable(); 

int level = ???; //业务逻辑得出一个level信息,比如当前电量
levelListDrawable.setLevel(level); //根据得出的电量level信息显示level-list中的相应电量的图片

6.<transition/>

<transition xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable=""
        android:id=""
        android:left=""
        android:top=""
        android:right=""
        android:bottom="" />
</transition>

两个Drawable之间的淡入淡出效果,属性同前面的Drawable,示例代码:

<?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>
//id是text_view的TextView用上面的transition做背景
TextView tv = (TextView) findViewById(R.id.text_view); 
TransitionDrawable drawable = (TransitionDrawable) tv.getDrawable(); 

drawable.startTransition(1000);
//drawable.reverseTransition(1000);//逆过程

7.<inset/>

<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable=""
    android:insetLeft=""
    android:insetTop=""
    android:insetRight=""
    android:insetBottom="" />

将其他drawable内嵌到inset中,并且可以在四周留出一定的间距,属性和前面的Drawable类似。下面的示例代码实现了inset中的shape距离View的边界为15dp,layer-list可以实现相同效果:

<?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.<scale/>

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable=""
    android:scaleGravity=""
    android:scaleHeight=""
    android:scaleWidth="" />

android:scaleGravity等同于shape中的android:gravity,android:scaleWidth和android:scaleHeight分别表示对指定drawable宽和高的缩放比例,以百分比的形式表示(看下面的示例代码)。使用scale的时候需要考虑ScaleDrawable的level值,levle是0的时候表示ScaleDrawable不可见,0也是默认值,所以要想ScaleDrawable可见,level等级不能是0。示例代码如下:

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/test_drawable"
    android:scaleGravity="center"
    android:scaleHeight="70%"
    android:scaleWidth="70%" />
View view = findViewById(R.id.test_view);
ScaleDrawable drawable = (ScaleDrawable)view.getBackground();
drawable.setLevel(1);

代码中必须设置level值,否则默认值0是不可见的。level范围系统内部约定为0-10000,当然设置成20000也能正常工作但不推荐那样做。

9.<clip/>

<clip xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable=""
    android:clipOrientation=""
    android:gravity="" />

clipOrientation表示裁剪方向,有水平和竖直两个方向。gravity和clipOrientation一起才能发挥作用,gravity的值如下:

含义
top 将内部Drawable放在容器顶部,不改变它的大小。如果竖直裁剪,那么从底部开始裁剪
bottom 将内部Drawable放在容器底部,不改变它的大小。如果竖直裁剪,那么从顶部开始裁剪
left 将内部Drawable放在容器左边,不改变它的大小。如果水平裁剪,那么从右边开始裁剪
这是默认值
right 将内部Drawable放在容器右边,不改变它的大小,如果水平裁剪,那么从左边开始裁剪
center_vertical 将内部Drawable在容器中竖直居中,不改变大小,如果竖直裁剪,那么从上下同时开始裁剪
fill_vertical 将内部Drawable竖直方向填充容器。如果为竖直裁剪,那么仅当ClipDrawable的等级为0时(0表示ClipDrawable被完全裁剪,即不可见),才能有裁剪行为
center_horizontal 类似center_vertical,方向不同
fill_horizontal 类似fill_vertical,方向不同
center 将内部Drawable水平和竖直方向都居中,不改变大小。如果竖直裁剪,那么从上下同时裁剪,如果水平裁剪,那么从左右同时裁剪
fill 将内部Drawable水平和竖直方向同时填充,仅当ClipDrawable的等级为0时,才有裁剪行为
clip_vertical 附加选项,表示竖直方向裁剪,较少使用
clip_horizontal 附加选项,表示水平方向裁剪,较少使用

Drawable等级是有范围的,即0-10000,最小值0表示完全裁剪,即整个Drawable都不可见。最大值10000表示不裁剪。如果竖直方向从上向下裁剪,level值是8000表示裁剪了2000,即在顶部裁剪掉20%的区域,被裁剪的区域就相当于不存在了。



三、Drawable的 level 总结

上面xml属性介绍中有些drawable中level是很重要的,这里总结一下:

  • <level-list/> 中有多个item,每个item对应一个drawable,通过设置具体的level值来决定使用哪个item即drawable。
  • <scale/> 用于缩放,level默认值是0,0表示ScaleDrawable不可见,所以要想ScaleDrawable可见,必须设置level不为0,具体是几无所谓,不为0即可
  • <clip/> 表示裁剪,level值决定裁剪百分比,需要具体值,因为决定裁剪百分比

level值的范围系统规定0-10000,设置level值的方法:

  1. 将相应的Drawable设置成一个View的背景
  2. 从View的背景中取得相应Drawable对象,代码view.getDrawable() 或 view.getBackground(),强转成相应的Drawable类型即可
  3. 取得Drawable对象后调用setLevel()方法设置level。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,046评论 25 707
  • 概述 今天我们来探究一下android的样式。其实,几乎所有的控件都可以使用 background属性去引用自定义...
    CokeNello阅读 4,794评论 1 19
  • 前言 本文是本人阅读《Android开发艺术探索》的第6章《Android的Drawable》后的总结笔记。包含了...
    daking阅读 5,288评论 2 29
  • 一、 Drawable简介 1 Drawable表示的是一种可以在Canvas上进行绘制的抽象的概念,可以是纯颜色...
    黄海佳阅读 1,190评论 0 10
  • 经常听人说“一胖毁所有”,其实,肥胖不只毁了美感,还毁了健康。它不仅会成为心脑血管疾病的“导火索”,更是引起人类致...
    顶誉会阅读 460评论 0 0