Drawable 是开发中经常用到的一个概念,我们经常用它去设置 View 的背景,背景可以一个颜色值,也可以是一张资源图片,还可以是一个自定义的 Drawable等等。这篇文章就简单说下 Drawable 与 View 的关系,同时结合代码,简要分析一下 Drawable 如何作用于 View。

Drawable 介绍

官方介绍

A Drawable is a general abstraction for "something that can be drawn." Most often you will deal with Drawable as the type of resource retrieved for drawing things to the screen; the Drawable class provides a generic API for dealing with an underlying visual resource that may take a variety of forms. Unlike a View, a Drawable does not have any facility to receive events or otherwise interact with the user.

简单翻译下:

Drawable 是 “所有可绘制东西” 的一个抽象,大多数时候,我们只需要把各种不同类型的资源作为转化为 drawable,然后 View 会帮我们把它渲染到屏幕上。Drawable 类提供了一个通用 API,用于解析转化各种可视资源到 Canvas,跟 View 不一样,Drawable 不能接受任何事件以及用户交互

总而言之,Drawable 就是一个可绘制东西的抽象,相比 View,它更纯粹,就是用来做绘制相关事情的,它处理不了用户交互事件,也不需要处理,所有交互相关的事都是由 View 来完成,但是背景相关的事大都可以通过 Drawable 来完成。

一般的,我们要为 View 设置背景,可通过如下几种方式:

  • 通过颜色为 View 设置背景
  • 通过自定义的 shape 设置背景

用颜色设置背景

通过 View 的 setBackgroundColor 方法可以设置颜色为 View 的背景。

button.setBackgroundColor(Color.YELLOW);

效果如下:

用自定义的 shape 设置背景

先用 xml 自定义一个圆角空心描边矩形 shape

<shape
android:shape="rectangle">
<corners android:radius="4dp"/>
<solid android:color="#fff"/>
<stroke android:color="#ef6f06" android:width="1dp"/>
</shape>

通过代码进行设置

button.setBackgroundResource(R.drawable.bk_normal);

效果如下:

可以看到,给 View 设置背景 drawable 非常简单,具体通过如下的 API 实现背景设置:

  • setBackgroundColor(@ColorInt int color)
  • setBackgroundResource(@DrawableRes int resid)
  • setBackground(Drawable background)

但是设置的背景 Drawable 是如何在 View 上生效的,可能很多人没去思考过这个问题,这里简单分析下。

Drawable 如何作用于 View

Drawable 是一个抽象类,这里通过它的的几个抽象方法就能大概猜得出 Drawable 如何作用于 View,下面是 Drawable 的几个抽象方法:

public abstract void draw(@NonNull Canvas canvas);
public abstract void setAlpha(@IntRange(from=0,to=255) int alpha);
public abstract void setColorFilter(@Nullable ColorFilter colorFilter);
public abstract @PixelFormat.Opacity int getOpacity();

可以看到这里有一个 draw 方法,并且参数中提供了 canvas 对象。

public abstract void draw(@NonNull Canvas canvas);

现在可以想象一下,View 通过 setBackground 方法为自己设置了一个 drawable 对象后,而 drawable 又有一个 draw 方法,那么 View 绘制自己的背景时,直接调用 drawable 对象的的 draw 方法,这个 draw 方法需要一个 canvas 对象,这里可直接把 View 的 Canvas 对象传递过去,那么 Drawable 就可以成功的把自己的绘制内容应用到 View 之上。

这个过程,相当于 View 把自己的背景绘制功能外包给了 Drawable 对象。

而且,这也是一种非常好的设计模式,View 负责测量自己大小,给自己指定位置,并绘制 View 前景 ,但是把自己的背景绘制外派给了更独立的 drawable 去做,从而做到了让自己更加轻量,现在 View 就成功的把背景绘制职责分配给了自己的 drawable 对象。

尽管上面只是想象,但事实上也确实如此。通过查看源码,在 View 中有一个私有方法 drawBackground,它的作用就是把 drawable 绘制在 canvas 上。

/**
* Draws the background onto the specified canvas.
* @param canvas Canvas on which to draw the background
*/
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
setBackgroundBounds();
//省略部分代码
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
//调用 drawable 自己的 draw 方法,从而将绘制的功能移交到 drawable 类
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}

Drawable 与 View 的关系

  • View 是皮,它是一个具体的东西,看得见摸得着,因为它自己可以测量自己打消、指定自己位置,还能接受 onTouch 事件从而处理用户交互。
  • Drawable 是毛,它可以不存在,因为 View 完全可以在自己的 onDraw 时机中,自己把自己绘制了,无需把绘制进行外包。
  • 但是 Drawable 更专业,更独立,它提供了一整套丰富的背景 Drawable 机制,它有丰富的实现类,可以提供给 View 进行方便的背景设置,对 View 来说 drawable 提供的那些实现类开箱即用,还可以减少自己的职能,节省自己的维护开销,何乐而不为。

总结

Drawable 是一个抽象的概念,只要理解了它跟 View 的关系,其实 Drawable 的想象力会非常大。通过自定义 Drawable,可以在 Drawable 中完成各种绘制逻辑,自定义完成后,只需要让 View 调用 setBackground() 方法,把自定义的 Drawable 传递进去,这样就可以方便把自定义 Drawable 和 View 关联在一起。

之前写过一个转菊花的 Loading 效果,就是用自定义 Drawable 实现的,目前已开源在 github,感兴趣的去看看。

FlowerLoading: Android loading or progress view, just like iOS IndicatorView.

关于作者

咕咚,Android 工程师,个人博客 gudong.name

本篇文章由一文多发平台ArtiPub自动发布

Android Drawable 简析的更多相关文章

  1. Android MVP 简析

    原地址:https://segmentfault.com/a/1190000003927200 在Android中应用MVP模式的原因与方法的简析,写的简单易懂.

  2. Android : SELinux 简析&修改

    一 SELinux背景知识 SELinux出现之前,Linux上的安全模型叫DAC,全称是Discretionary Access Control,翻译为自主访问控制.DAC的核心思想很简单,就是: ...

  3. Android View 简析

    基于android 4.4上源码分析: setContentView流程: getwindow() ->setContentView() -> installDecor() -> a ...

  4. android:installLocation简析

    在Froyo(android 2.2,API Level:8)中引入了android:installLocation.通过设置该属性可以使得开发者以及用户决定程序的安装位置. android:inst ...

  5. Android -- ImageLoader简析

    图片的内存缓存实现 Image-Loader库有一个较完整的内存缓存实现,使用者可以根据需要选择已经实现的策略,也可以定制自己项目中需要的策略. 内存缓存实现代码在memory和memory.impl ...

  6. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  7. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  8. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  9. zxing二维码扫描的流程简析(Android版)

    目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷... 下载下来后定位两个文件夹,core和android,core是一些核心的库,android是 ...

随机推荐

  1. Timed out after 30000 ms while waiting to connect

    今天使用mongo-java-drive写连接mongo的客户端,着实被上面那个错坑了一把.回顾一下解决过程: 报错: com.mongodb.MongoTimeoutException: Timed ...

  2. 解决window.onload延迟加载问题

    window.onload方法,表示当页面所有的元素都加载完毕,并且所有要请求的资源也加载完毕才触发执行function这个匿名函数里边的具体内容.这样肯定保证了代码在domReady之后执行.使用w ...

  3. 洛谷 P2145 [JSOI2007]祖码

    题目描述 这是一个流行在Jsoi的游戏,名称为祖玛. 精致细腻的背景,外加神秘的印加音乐衬托,彷佛置身在古老的国度里面,进行一个神秘的游戏——这就是著名的祖玛游戏.祖玛游戏的主角是一只石青蛙,石青蛙会 ...

  4. 线程池和lambda表达式

    线程池1.什么是线程池.一个用来创建和管理线程的容器;2.线程池的作用.提高线程的复用性,降低资源消耗提高线程的响应速度,提高线程的可管理性3.线程的核心思想;线程的复用 4.线程池的创建Execut ...

  5. Kubernetes集群的部署方式及详细步骤

    一.部署环境架构以及方式 第一种部署方式 1.针对于master节点 将API Server.etcd.controller-manager.scheduler各组件进行yum install.编译安 ...

  6. Flask中的数据连接池

    pymsql链接数据库 import pymysql conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd=' ...

  7. Python3字符串常见方法

    目录 字符串的进阶使用 格式化输出字符串 当然除了上述方法外,还可以你使用format方法 format方法第二种用法: Python字符串与二进制的转换 字母大写 计字符a出现的次数 输出50个字符 ...

  8. 基于powershell的socks代理

    0x01 前言 在实战中,内网的代理尤其重要,而常见的端口转发被反病毒软件已经盯死,那么学习使用基于powershell的渗透脚本进行代理.端口转发的非常有必要的. 0x02 使用 介绍ithub: ...

  9. 内网转发之reGeorg+proxifier

    先将reGeorg的对应脚本上传到服务器端,reGeorg提供了PHP.ASPX.JSP脚本,直接访问显示“Georg says, 'All seems fine'”,表示脚本运行正常. 运行 pyt ...

  10. 修改配置文件application.properties

    附录A.常用应用程序属性 可以在application.properties文件内部application.yml,文件内部或命令行开关中指定各种属性.本附录提供了常见Spring Boot属性的列表 ...