Android 开发艺术探索笔记之六 -- Android 的 Drawable

整理一下,基本只作为 知识清单 使用

学习内容:

  • Drawable 的层次关系
  • Drawable 分类
  • 自定义 Drawable 的相关知识

Drawable 简介

Drawable 表示的是一种可以在 canvas 上进行绘制的图像的 抽象概念。实际开发中,Drawable 常被用来作为 View 的背景使用。

优点:

  1. 使用简单,比自定义 View 的成本低
  2. 非图片类型的 Drawable 占用空间较小,有利于减小 apk 的大小。

层次关系:

  1. Drawable 是一个抽象类,是所有 Drawable 对象的基类。
  2. 每个具体的 Drawable 都是它的子类,比如 ShapeDrawable,BitmapDrawable 等。

Drawable 内部宽 / 高

  1. 通过 getIntrinsicWidth 和 getIntrinsicHeight 两个方法获取。
  2. 并非所有 Drawable 都有内部宽/高(图片形成的 Drawable 内部宽/高等同于图片的宽/高,颜色形成的 Drawable 没有内部宽/高的概念)
  3. Drawable 的内部宽/高不等同于它的大小,一般来说,Drawable 没有大小概念,当作为 View 背景时,会被拉甚至 View 的同等大小。

Drawable 分类

1.BitmapDrawable

表示的就是一张图片,可以直接引用原始的图片,也可以通过 xml 方式描述它。

<?xml version="1.0" encoding="utf-8"?>
<bitmap  
  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:mipMap=["true"|"false"]
    android:tileMode=["disabled"|"clamp"|"repeat"|"mirror"]></bitmap>

属性说明:

  1. android:src:图片的资源 ID
  2. android:antialias:抗锯齿,牺牲清晰度使图片平滑。建议开启
  3. android:dither:抖动效果,当图片像素配置和手机屏幕的像素配置不一致时,开启此选项可以让高质量图片在低质量屏幕上还能保持较好的小时效果。建议开启
  4. android:filter:过滤效果,图片拉伸或压缩时,开启此选项可以保持较好的显示效果。建立开启
  5. android:gravity:当图片小于容器的尺寸时,设置此选项对图片进行定位,多个属性值可以通过 "|" 来组合使用
  6. android:mipMap:纹理映射,默认值为 false,日常开发中不涉及此项
  7. android:tileMode:平铺模式,默认为 disabled,关闭平铺模式;clamp 表示图片四周的像素扩散到周围区域;repeat 表示简单的水平和垂直方向上的平铺效果;mirror 表示一种水平和竖直方向上的镜面投影效果;另外需要注意,当开启平铺模式后, gravity 属性将被忽略。

2.ShapeDrawable

可以理解为通过颜色来构造的图形,既可以是纯色的图形,也可以是具有渐变效果的图形。

<?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:bottomLeftRadius="integer"
        android:bottomRightRadius="integer"
        android:radius="integer"
        android:topLeftRadius="integer"
        android:topRightRadius="integer" />

    <gradient
        android:angle="integer"
        android:centerColor="integer"
        android:centerX="integer"
        android:centerY="integer"
        android:endColor="color"
        android:gradientRadius="integer"
        android:startColor="color"
        android:type=["linear"|"radial"|"sweep"]
        android:useLevel=["false"|"false"] />

    <padding
        android:left="integer"
        android:right="integer"
        android:top="integer"
        android:bottom="integer"/>
    
    <size
        andorid:width="integer"
        andorid:height="integer"/>
    
    <solid android:color="color" />

    <stroke
        android:width="integer"
        android:color="color"
        android:dashGap="integer"
        android:dashWidth="integer" />
</shape>

属性说明:

  1. android:shape:表示图形的形状,有四种:rectangle矩形,oval椭圆,line横线,ring圆环。
    1. 默认为矩形
    2. line 和 ring 必须通过 <stroke> 指定线的宽度和颜色等信息
    3. ring 有特殊的5个属性:
      1. android:innerRadius:圆环内半径,优先级大于android:innerRadiusRatio
      2. android:thickness:圆环的厚度,优先级大于 android:thicknessRatio
      3. android:innerRadiusRatio:内半径占整个 Drawable 宽度的比例
      4. android:thicknessRatio:厚度占整个 Drawable 宽度的比例
      5. android:useLevel:一般都使用 false,除非当作 LevelListDrawable 来使用。
  2. <corners>:表示 shape 的四个角的圆角角度,单位是 px,只适用于 矩形 shape。有如下 5 个属性:
    1. android:radius:为四个角同时设定相同角度,优先级低。
    2. andorid:topLeftRadius:左上角的角度
    3. andorid:topRightRadius:右上角的角度
    4. andorid:bottomLeftRadius:左下角的角度
    5. andorid:bottomRightRadius:右下角的角度
  3. <gradient>:与 <solid> 互斥,solid 表示纯色填充,gradient 表示渐变效果,有以下属性:
    1. android:angle:渐变的角度,默认为0,其值必须是 45 的倍数,0表示从左往右,90 表示 从上到下。
    2. android:centerX:渐变的中心横坐标
    3. android:centerY:渐变的中心纵坐标
    4. android:startColor:渐变起始色
    5. android:centerColor:渐变的中间色
    6. android:endColor:渐变的结束色
    7. android:gradientRadius:渐变半径,仅当 android:type="radial" 时有效
    8. android:useLevel:一般为 false,当 Drawable 作为 StateListDrawable 使用时为 true
    9. android:type:渐变的类别,有 linear 线性渐变、radial 径向渐变、sweep 扫描线渐变三种,默认为 线性渐变
  4. <solid>:表示 纯色填充,通过 android:color 指定 填充的颜色
  5. <stroke>:Shape 的描边,有如下属性
    1. android:width:描边宽度
    2. android:color:描边颜色
    3. android:dashWidth:组成虚线的线段的宽度
    4. android:dashGap:组成虚线的线段之间的间隔
  6. <padding>:表示包含当前 Shape 的 View 的空白,有四个属性, android:left、android:right、android:top、android:bottom。
  7. <size>:shape 的大小,有两个属性:android:width 和 android:height,分别表示 shape 的固有宽/高,但是作为 View 的背景时,仍会被拉伸或者缩小为 View 的大小

3.LayerDrawable

对应的 XML 标签是 <layer-list>,表示一种层次化的 Drawable 集合。一个 layer-list 可以包含多 个item,每一个 item 表示一个 Drawable。

Layer-list 有层次的概念,下面的 item 会覆盖上面的 item,即会分层与叠加。

<?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:bottom="dimenison"
        android:left="dimenison"
        android:right="dimenison"
        android:top="dimenison" />
    
</layer-list>

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=["true"|"false"]
    android:dither=["true"|"false"]
    android:variablePadding=["true"|"false"]>
    <item 
        android:drawable="@[package:]drawable/drawable_resource"
        android:state_pressed=["true"|"false"]
        android:state_foucused=["true"|"false"]
        android:state_hoverd=["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>

相关属性介绍:

  1. android:constantSize:固有大小是否不随着其状态的改变而改变,因为状态的改变会导致 StateListDrawable 切换到具体的 Drawable,而不同的 Drawable 有不同的固有大小,true 表示 固有大小保持不变,此时它的固有大小是内部所有 Drawable 的固有大小的最大值。默认值为 false
  2. android:dither:是否开启抖动效果。
  3. android:variablePadding:padding 是否随着其状态的改变而改变。类似 constantSize 属性。
  4. 状态相关
    1. android:state_pressed:按下状态,此时按下尚未松开
    2. android:state_focused:获取了焦点
    3. android:state_selected:用户选择了 view
    4. android:state_checked:用户选中了 view
    5. android:state_enabled:view 当前处于可用状态

5.LevelListDrawable

对应于 <level-list> 标签,同样表示 Drawable 集合,集合中的每个 Drawable 都有一个 等级level 的概念,根据不同等级,切换对应的 Drawable。

每个 item 对应一个 Drawable,并且由 android:maxLevel 和 android:minLevel 指定对应的等级范围。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/drawable_resource" 
          android:maxLevel="integer"
          android:minLevel="integer"/>
</selector>

6.TransitionDrawable

对应于 <transition> 标签,用于实现两个 Drawable 之间的淡入淡出的效果。

<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@[package:]drawable/drawable_resource"
        android:id="@[+][package:]id/resource_name"
        android:bottom="dimenison"
        android:left="dimenison"
        android:right="dimenison"
        android:top="dimenison"/>
</transition>

通过将 TransitionDrawable 设置为 view 的背景,通过view 的 startTransition 和 reverseTransition 方法实现淡入淡出的效果以及其逆过程。

7.InsetDrawable

对应于 <inset> 标签,可以将其他 Drawable 内嵌到自己当中,并可以在四周留出一定的间距。当一个 View 希望自己的背景比自己的实际区域小的时候,可以采用 InsetDrawable 来实现。

<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/drawable_resource"
    android:insetBottom="dimension"
    android:insetLeft="dimension"
    android:insetRight="dimension"
    android:insetTop="dimension">
</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="@drawable/drawable_resource"
    android:scaleGravity=["top"|"bottom"|"left"|"right"|"center_vertical"|"fill_vertical"|"center_horizontal"|"fill_horizontal"|"center"|"fill"|"clip_vertical"|"clip_horizontal"]
    android:scaleHeight="percentage"
    android:scaleWidth="percentage">
</scale>

定义的缩放比例越大,那么内部 Drawable 看起来越小。

除了定义上述 Drawable,还需要设置等级。

ScaleDrawable scaleDrawable = (ScaleDrawable) imageView.getBackground();
caleDrawable.setLevel(1);

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=["horizontal"|"vertical"]
    android:drawable="@drawable/drawable_resource"
    android:gravity=["top"|"bottom"|"left"|"right"|"center_vertical"|"fill_vertical"|"center_horizontal"|"fill_horizontal"|"center"|"fill"|"clip_vertical"|"clip_horizontal"]>
</clip>

仍需要在代码中设置 ClipDrawable 的等级。等级的范围是 0~10000,0 表示 完全裁剪,10000 表示不裁剪。

ClipDrawable clipDrawable = (ClipDrawable) ivLevei.getBackground();
clipDrawable.setLevel(7000);

自定义 Drawable

通常下,没有必要自定义 Drawable,因为自定义的 Drawable 无法在 xml 中使用。

核心方法:draw(),setAlpha(),setColorFilter(),sgetOpacity()。

当自定义的 Drawable 由固有大小时最好重写 getInntrinsicWidth 和 getintrinsicHeight ,因为二者会影响到 view 的 wrap_content 布局。

内部大小不等于 Drawable 的实际区域大小,Drawable 实际区域大小可以通过 getBounds 方法得到,一般个它的 View 的尺寸相同。

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

推荐阅读更多精彩内容