Android View框架的draw机制
概述
Android中View框架的工作机制中,主要有三个过程:
1、View树的测量(measure) Android View框架的measure机制
2、View树的布局(layout)Android View框架的layout机制
3、View树的绘制(draw)Android View框架的draw机制
View框架的工作流程为:测量每个View大小(measure)-->把每个View放置到相应的位置(layout)-->绘制每个View(draw)。
本文主要讲述三大流程中的draw过程。不清楚measure过程的,可以参考这篇文章 Android View框架的measure机制。
不清楚layout过程的,可以参考这篇文章Android View框架的layout机制。
这篇文章不讲述graphic模块具体绘制API的使用,只是描述View框架的绘制过程。
带着问题来思考整个draw过程。
1、系统为什么要有draw过程?
View框架在经过了measure过程和layout过程之后,就已经确定了每一个View的尺寸和位置。那么接下来,也是一个重要的过程,就是draw过程,draw过程是用来绘制View的过程,它的作用就是使用graphic框架提供的各种绘制功能,绘制出当前View想要的样子。
2、draw过程都干了点什么事?
View框架中,draw过程主要是绘制View的外观。ViewGroup除了负责绘制自己之外,还需要负责绘制所有的子View。而不含子View的View对象,就负责绘制自己就可以了。
draw过程的主要流程如下:
1、绘制 backgroud(drawBackground)
2、如果需要的话,保存canvas的layer,来准备fading(不是必要的步骤)
3、绘制view的content(onDraw方法)
4、绘制children(dispatchDraw方法)
5、如果需要的话,绘制fading edges,然后还原layer(不是必要的步骤)
6、绘制装饰器、比如scrollBar(onDrawForeground)
源代码分析
在View的源代码中,提取到了下面一些关于layout过程的信息。
我们知道,整棵View树的根节点是DecorView,它是一个FrameLayout,所以它是一个ViewGroup,所以整棵View树的测量是从一个ViewGroup对象的draw方法开始的。
View:
1、draw
/**
绘制一个View以及他的子View。最好不要覆写该方法,应该覆写onDraw方法来绘制自己。
*/
public void draw(Canvas canvas);
源代码:
这里不给出,有兴趣的读者,可以自行查阅SDK。
伪代码
public void draw(Canvas canvas) {
1、绘制 backgroud(drawBackground) ;
2、如果需要的话,保存canvas的layer,来准备fading ;
3、绘制view的content(onDraw方法);
4、绘制children(dispatchDraw方法);
5、如果需要的话,绘制fading edges,然后还原layer ;
6、绘制装饰器、比如scrollBar(onDrawForeground);
}
2、onDraw
/**
绘制一个View的外观。View的默认实现是空实现,所以这里没有源码给出。
*/
protected void onDraw(Canvas canvas);
ViewGroup:
1、dispatchDraw
/** 绘制子View,View类是空实现,ViewGroup类中有实现 */
protected void dispatchDraw(Canvas canvas);
源代码:
这里不再给出,有兴趣的读者自行查阅SDK。
伪代码:
protected void dispatchDraw(Canvas canvas) {
if (需要绘制布局动画) {
for (遍历子View) {
绑定布局动画;
}
启动动画控制,通知动画开始;
}
for (遍历子View) {
child.draw();
}
}
动手操作
下面我们自己写一个自定义的ViewGroup,让它内部的每一个子View都垂直排布,并且让每一个子View的左边界都距离上一个子View的左边界一定的距离。然后在整个布局内部绘制一个圆形。大概看起来如下图所示:

实际运行效果如下:

代码如下:
public class VerticalOffsetLayout extends ViewGroup {
private static final int OFFSET = 100;
private Paint mPaint;
public VerticalOffsetLayout(Context context) {
super(context);
init(context, null, 0);
}
public VerticalOffsetLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public VerticalOffsetLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mPaint = new Paint(Color.BLUE);
mPaint.setAntiAlias(true);
mPaint.setAlpha(125);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
ViewGroup.LayoutParams lp = child.getLayoutParams();
int childWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
int childHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
child.measure(childWidthSpec, childHeightSpec);
}
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
int widthAddOffset = i * OFFSET + child.getMeasuredWidth();
width = Math.max(width, widthAddOffset);
}
break;
default:
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
height = height + child.getMeasuredHeight();
}
break;
default:
break;
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
int right = 0;
int top = 0;
int bottom = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
left = i * OFFSET;
right = left + child.getMeasuredWidth();
bottom = top + child.getMeasuredHeight();
child.layout(left, top, right, bottom);
top += child.getMeasuredHeight();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getWidth()/2;
int y = getHeight()/2;
canvas.drawCircle(x, y, Math.min(x, y), mPaint);
}
}
Android View框架的draw机制的更多相关文章
- Android View框架的measure机制
概述 Android中View框架的工作机制中,主要有三个过程: 1.View树的測量(measure)Android View框架的measure机制 2.View树的布局(layout) Andr ...
- Android View框架的layout机制
概述 Android中View框架的工作机制中,主要有三个过程: 1.View树的测量(measure) Android View框架的measure机制 2.View树的布局(layout)Andr ...
- Android View框架总结(八)ViewGroup事件分发机制
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52298780 上篇分析了View的事件分发流程,留了一个问题:如果上 ...
- Android View的事件分发机制探索
概述 Android事件传递机制也是Android系统中比较重要的一块,事件类型有很多种,这里主要讨论TouchEvent的事件在framework层的传递处理机制.因为对于App开发人员来说,理解f ...
- Android View框架总结(四)View布局流程之Measure
View树的measure流程 View的measures时序图 View布局流程之measure measure过程 View的measure过程 ViewGroup的measure过程 Frame ...
- Android View框架总结(七)View事件分发机制
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52282833 View布局告一段落,从本篇开始View事件相关分析, ...
- Android view 的事件分发机制
1 事件的传递顺序是 Activity -> Window -> 顶层View touch 事件产生后,最先由 activity 的 dispatchTouchEvent 处理 /** * ...
- Android View框架总结(二)View焦点
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52263256 前言:View框架写到第六篇,发现前面第二篇竟然没有, ...
- Android View框架总结(三)View工作原理
转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52180375 测量/布局/绘制顺序 如何引起View的测量/布局/绘制? Perfor ...
随机推荐
- k8s系列--- dashboard认证及分级授权
http://blog.itpub.net/28916011/viewspace-2215214/ 因版本不一样,略有改动 Dashboard官方地址: https://github.com/kube ...
- delphi制作res文件
第一步:将brcc32.exe拷贝到某个目录,如“res文件”第二步:制作rc文件1.在“res文件”中新建一个文本文件resources.rc:2.文本文件中每一行写一个资源,资源格式为:资源标识名 ...
- Webpack之optimization.splitChunks代码分割插件的配置
SplitChunkPlugin插件配置参数详解 对引入的库代码(例如:lodash.jQuery等)进行代码的分割进行优化 若配置时只写chunks:"all",其余则为默认配置 ...
- Ubuntu 18.04 MATLAB 安装及配置
转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12367846.html 本文要点: Ubuntu 18.04 安装 ...
- 用C语言实现中国象棋
基于五子棋框架上的 象棋 小游戏 本游戏是上各种水课无聊时的产物...不参考现有游戏从零开始实现各项功能. 游戏配置:二维数组,循环系统,wasd基本移动,调整窗台的函数,以及富足的发呆时间.. 完整 ...
- java架构之路-(dubbo专题)dubbo的基本使用
今天我们来说一下dubbo的基本使用,没有什么高阶的知识,真的超级简单易懂. Dubbo核心功能解释 dubbo 阿里开源的一个SOA服务治理框架,从目前来看把它称作是一个RPC远程调用框架更为贴切. ...
- Spark组件间通信
1.Spark组件之间使用RPC机制进行通信.RPC的客户端在本地编写并调用业务接口,接口在本地通过RPC框架的动态代理机制生成一个对应的实现类,在这个实现类中完成soket通信.远程调用等功能的逻辑 ...
- 【读书笔记】https://source.android.google.cn/devices/bootloader
https://source.android.google.cn/devices/bootloader 本文主要记录aosp官网关于bootloader的相关资料 Bootloader A bootl ...
- 【WPF学习】第十七章 鼠标输入
鼠标事件执行几个关联的任务.当鼠标移到某个元素上时,可通过最基本的鼠标事件进行响应.这些事件是MouseEnter(当鼠标指针移到元素上时引发该事件)和MouseLeave(当鼠标指针离开元素时引发该 ...
- CSP2019第二轮-划水游记
又是 颓废的 一年 眨眼间已经初三了 到了NOIPCSP的时节 Day0 学校(没错,全校事件)抽风地把二晚停了,于是也就有了机会早早回家van♂耍 和母上大人简单地收拾收拾东西,回了姥姥家 以&qu ...