Android Drawable 简析
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 简析的更多相关文章
- Android MVP 简析
原地址:https://segmentfault.com/a/1190000003927200 在Android中应用MVP模式的原因与方法的简析,写的简单易懂.
- Android : SELinux 简析&修改
一 SELinux背景知识 SELinux出现之前,Linux上的安全模型叫DAC,全称是Discretionary Access Control,翻译为自主访问控制.DAC的核心思想很简单,就是: ...
- Android View 简析
基于android 4.4上源码分析: setContentView流程: getwindow() ->setContentView() -> installDecor() -> a ...
- android:installLocation简析
在Froyo(android 2.2,API Level:8)中引入了android:installLocation.通过设置该属性可以使得开发者以及用户决定程序的安装位置. android:inst ...
- Android -- ImageLoader简析
图片的内存缓存实现 Image-Loader库有一个较完整的内存缓存实现,使用者可以根据需要选择已经实现的策略,也可以定制自己项目中需要的策略. 内存缓存实现代码在memory和memory.impl ...
- Android RecycleView + CardView 控件简析
今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...
- Java Android 注解(Annotation) 及几个常用开源项目注解原理简析
不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...
- Android 启动过程简析
首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...
- zxing二维码扫描的流程简析(Android版)
目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷... 下载下来后定位两个文件夹,core和android,core是一些核心的库,android是 ...
随机推荐
- Python多任务之协程
前言 协程的核心点在于协程的使用,即只需要了解怎么使用协程即可:但如果你想了解协程是怎么实现的,就需要了解依次了解可迭代,迭代器,生成器了: 如果你只想看协程的使用,那么只需要看第一部分内容就行了:如 ...
- 【TencentOS tiny】深度源码分析(7)——事件
引言 大家在裸机编程中很可能经常用到flag这种变量,用来标志一下某个事件的发生,然后在循环中判断这些标志是否发生,如果是等待多个事件的话,还可能会if((xxx_flag)&&(xx ...
- homebrew安装问题(Failed during: git fetch origin master:refs/remotes/origin/master --tags --force)
在mac系统中,使用homebrew可以很方便的管理包.按照官网的说明执行以下命令时总是报错: /usr/bin/ruby -e "$(curl -fsSL https://raw.gith ...
- strcpy()、strncpy()和memcpy()对比
strcpy()函数声明:char *strcpy(char *dest, const char *src)返回参数:指向最终的目标字符串 dest 的指针.注意事项:只能复制char类型的字符数组, ...
- webshell之一句话木马变形
什么是一句话木马 一句话木马就是只需要一行代码的木马,短短一行代码,就能做到和大马相当的功能.为了绕过waf的检测,一句话木马出现了无数中变形,但本质是不变的:木马的函数执行了我们发送的命令. 我们如 ...
- powerCat进行常规tcp端口转发
实战中,我们也会遇到需要我们进行端口转发的情况,比如已经拿下的目标机1是在dmz区,而目标1所在内网的其他目标只能通过目标1去访问,这时候我们就需要端口转发或者代理来进行后渗透.这次就要介绍一个加强版 ...
- C++中哪些函数不能声明为inline?
inline关键字仅仅是对编译器的建议,编译器有权力决定一个函数是否在调用处嵌入.因为内联函数要在调用处展开,编译器必须能在每一个调用处能看到该函数的定义,因此最好将函数实现放在头文件中(而且实现在类 ...
- Python开发【第五篇】字符串
字符串 作用:用来记录文字信息 例子: 空字符串 '' #单引号空字符串 "" #双引号空字符串 ''' ''' #三单引号空字符串 """ &quo ...
- React学习系列之(1)简单的demo(React脚手架)
1.什么是React? React是一个一个声明式,高效且灵活的用于构建用户界面的JavaScript库.React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 ...
- Js正则学习笔记
众所周知正则表达式是十分强大的存在,编码时能够熟练使用正则能够极大的简化代码,因此掌握正则非常有必要. 创建正则语法: // 创建正则的两种方式// 1.构造函数 let reg = new RegE ...