概述

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机制的更多相关文章

  1. Android View框架的measure机制

    概述 Android中View框架的工作机制中,主要有三个过程: 1.View树的測量(measure)Android View框架的measure机制 2.View树的布局(layout) Andr ...

  2. Android View框架的layout机制

    概述 Android中View框架的工作机制中,主要有三个过程: 1.View树的测量(measure) Android View框架的measure机制 2.View树的布局(layout)Andr ...

  3. Android View框架总结(八)ViewGroup事件分发机制

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52298780 上篇分析了View的事件分发流程,留了一个问题:如果上 ...

  4. Android View的事件分发机制探索

    概述 Android事件传递机制也是Android系统中比较重要的一块,事件类型有很多种,这里主要讨论TouchEvent的事件在framework层的传递处理机制.因为对于App开发人员来说,理解f ...

  5. Android View框架总结(四)View布局流程之Measure

    View树的measure流程 View的measures时序图 View布局流程之measure measure过程 View的measure过程 ViewGroup的measure过程 Frame ...

  6. Android View框架总结(七)View事件分发机制

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52282833 View布局告一段落,从本篇开始View事件相关分析, ...

  7. Android view 的事件分发机制

    1 事件的传递顺序是 Activity -> Window -> 顶层View touch 事件产生后,最先由 activity 的 dispatchTouchEvent 处理 /** * ...

  8. Android View框架总结(二)View焦点

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52263256 前言:View框架写到第六篇,发现前面第二篇竟然没有, ...

  9. Android View框架总结(三)View工作原理

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52180375 测量/布局/绘制顺序 如何引起View的测量/布局/绘制? Perfor ...

随机推荐

  1. Hexo搭建静态博客踩坑日记(二)

    前言 Hexo搭建静态博客踩坑日记(一), 我们说到利用Hexo快速搭建静态博客. 这节我们就来说一下主题的问题与主题的基本修改操作. 起步 chrome github hexo git node.j ...

  2. shell脚本 定期删除日志

    定期删除日志: 然后建立清除日志文件的shell脚本,文件名为clean_log只保留最近三天的日志     #! /bin/bashlogdir=/var/log/httpdcd ${logdir} ...

  3. 在Unity中使用 luajit 64位加密

    参考:https://blog.csdn.net/sun19880421/article/details/68070696 https://blog.csdn.net/mydreamremindme/ ...

  4. python基础入门之四 —— 列表

    1.格式 [数据1,数据2,数据3,...] 列表可以一次性存多个数据,可以为不同的数据类型 2.下标 从0开始循序向下分配 3.常用函数 查找 index():返回指定数据所在位置下标,不存在就报错 ...

  5. Nginx三大主要功能

    1.做静态资源服务器,可以用于前端项目发布,图片文件文件等静态服务器. 2.做反向代理服务器,域名往往配置在Nginx上,真正的业务服务器躲在其身后. 3.做负载均衡服务器,作为负载集群的入口网关. ...

  6. 在centos7.x环境中SQL Server附加数据库

    第一步,准备好windows与Linux之间文件传递的工具,下载并安装 https://winscp.net/eng/download.php 第二步,把本地的数据库文件拷贝一份,放到别的文件夹中,因 ...

  7. centos 记录所有用户操作命令的脚本

    使用history不能看到所有用户的命令记录,如何看所有用户的操作记录. 如下: 在 /etc/profile 最下面加入如下代码即可. PS1="`whoami`@`hostname`:& ...

  8. 【Java】模拟登录教务网并获取数据

    本文章仅做技术交流演示学习,请勿用于违法操作! 前期准备 首先我们需要到要模拟登录的网页,进行抓包操作. 使用Chrome浏览器打开系统的登录页面,按F12打开开发者工具 切换到Network选项卡 ...

  9. Android进程永生技术终极揭秘:进程被杀底层原理、APP应对技巧

    1.引言 上个月在知乎上发表的由“袁辉辉”分享的关于TIM进程永生方面的文章(即时通讯网重新整理后的标题是:<史上最强Android保活思路:深入剖析腾讯TIM的进程永生技术>),短时间内 ...

  10. \n不换行

    \n在js中表示换行,<br/>在html中表示换行,所以如果在设置innerHtml值时使用  \n  ,那么在页面上并不会显示换行,而在设置innerText值时使用  \n  就会显 ...