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. Python多任务之协程

    前言 协程的核心点在于协程的使用,即只需要了解怎么使用协程即可:但如果你想了解协程是怎么实现的,就需要了解依次了解可迭代,迭代器,生成器了: 如果你只想看协程的使用,那么只需要看第一部分内容就行了:如 ...

  2. 【TencentOS tiny】深度源码分析(7)——事件

    引言 大家在裸机编程中很可能经常用到flag这种变量,用来标志一下某个事件的发生,然后在循环中判断这些标志是否发生,如果是等待多个事件的话,还可能会if((xxx_flag)&&(xx ...

  3. homebrew安装问题(Failed during: git fetch origin master:refs/remotes/origin/master --tags --force)

    在mac系统中,使用homebrew可以很方便的管理包.按照官网的说明执行以下命令时总是报错: /usr/bin/ruby -e "$(curl -fsSL https://raw.gith ...

  4. strcpy()、strncpy()和memcpy()对比

    strcpy()函数声明:char *strcpy(char *dest, const char *src)返回参数:指向最终的目标字符串 dest 的指针.注意事项:只能复制char类型的字符数组, ...

  5. webshell之一句话木马变形

    什么是一句话木马 一句话木马就是只需要一行代码的木马,短短一行代码,就能做到和大马相当的功能.为了绕过waf的检测,一句话木马出现了无数中变形,但本质是不变的:木马的函数执行了我们发送的命令. 我们如 ...

  6. powerCat进行常规tcp端口转发

    实战中,我们也会遇到需要我们进行端口转发的情况,比如已经拿下的目标机1是在dmz区,而目标1所在内网的其他目标只能通过目标1去访问,这时候我们就需要端口转发或者代理来进行后渗透.这次就要介绍一个加强版 ...

  7. C++中哪些函数不能声明为inline?

    inline关键字仅仅是对编译器的建议,编译器有权力决定一个函数是否在调用处嵌入.因为内联函数要在调用处展开,编译器必须能在每一个调用处能看到该函数的定义,因此最好将函数实现放在头文件中(而且实现在类 ...

  8. Python开发【第五篇】字符串

    字符串 作用:用来记录文字信息 例子: 空字符串 '' #单引号空字符串 "" #双引号空字符串 ''' ''' #三单引号空字符串 """ &quo ...

  9. React学习系列之(1)简单的demo(React脚手架)

    1.什么是React? React是一个一个声明式,高效且灵活的用于构建用户界面的JavaScript库.React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 ...

  10. Js正则学习笔记

    众所周知正则表达式是十分强大的存在,编码时能够熟练使用正则能够极大的简化代码,因此掌握正则非常有必要. 创建正则语法: // 创建正则的两种方式// 1.构造函数 let reg = new RegE ...