Android View体系(八)从源码解析View的layout和draw流程
前言
上一篇文章我们讲了View的measure的流程,接下来我们讲下View的layout和draw流程,如果你理解了View的measure的流程,那这篇文章自然就不在话下了。
1.View的layout流程
先来看看View的layout()方法:
- public void layout(int l, int t, int r, int b) {
- if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != ) {
- onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
- mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
- }
- int oldL = mLeft;
- int oldT = mTop;
- int oldB = mBottom;
- int oldR = mRight;
- boolean changed = isLayoutModeOptical(mParent) ?
- setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
- if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
- onLayout(changed, l, t, r, b);
- mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
- ListenerInfo li = mListenerInfo;
- if (li != null && li.mOnLayoutChangeListeners != null) {
- ArrayList<OnLayoutChangeListener> listenersCopy =
- (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
- int numListeners = listenersCopy.size();
- for (int i = ; i < numListeners; ++i) {
- listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
- }
- }
- }
- mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
- mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
- }
传进来里面的四个参数分别是View的四个点的坐标,它的坐标不是相对屏幕的原点,而且相对于它的父布局来说的。 l 和 t 是子控件左边缘和上边缘相对于父类控件左边缘和上边缘的距离;
r 和 b是子控件右边缘和下边缘相对于父类控件左边缘和上边缘的距离。来看看setFrame()方法里写了什么:
- protected boolean setFrame(int left, int top, int right, int bottom) {
- boolean changed = false;
- if (DBG) {
- Log.d("View", this + " View.setFrame(" + left + "," + top + ","
- + right + "," + bottom + ")");
- }
- if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
- changed = true;
- // Remember our drawn bit
- int drawn = mPrivateFlags & PFLAG_DRAWN;
- int oldWidth = mRight - mLeft;
- int oldHeight = mBottom - mTop;
- int newWidth = right - left;
- int newHeight = bottom - top;
- boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
- // Invalidate our old position
- invalidate(sizeChanged);
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
- ...省略
- }
- return changed;
- }
在setFrame()方法里主要是用来设置View的四个顶点的值,也就是mLeft 、mTop、mRight和 mBottom的值。在调用setFrame()方法后,调用onLayout()方法:
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- }
onLayout()方法没有去做什么,这个和onMeasure()方法类似,确定位置时根据不同的控件有不同的实现,所以在View和ViewGroup中均没有实现onLayout()方法。既然这样,我们就来看看LinearLayout的onLayout()方法:
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- if (mOrientation == VERTICAL) {
- layoutVertical(l, t, r, b);
- } else {
- layoutHorizontal(l, t, r, b);
- }
- }
layoutVertical做了什么呢?
- void layoutVertical(int left, int top, int right, int bottom) {
- final int paddingLeft = mPaddingLeft;
- int childTop;
- int childLeft;
- // Where right end of child should go
- final int width = right - left;
- int childRight = width - mPaddingRight;
- // Space available for child
- int childSpace = width - paddingLeft - mPaddingRight;
- final int count = getVirtualChildCount();
- final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
- final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
- switch (majorGravity) {
- case Gravity.BOTTOM:
- // mTotalLength contains the padding already
- childTop = mPaddingTop + bottom - top - mTotalLength;
- break;
- // mTotalLength contains the padding already
- case Gravity.CENTER_VERTICAL:
- childTop = mPaddingTop + (bottom - top - mTotalLength) / ;
- break;
- case Gravity.TOP:
- default:
- childTop = mPaddingTop;
- break;
- }
- for (int i = ; i < count; i++) {
- final View child = getVirtualChildAt(i);
- if (child == null) {
- childTop += measureNullChild(i);
- } else if (child.getVisibility() != GONE) {
- final int childWidth = child.getMeasuredWidth();
- final int childHeight = child.getMeasuredHeight();
- final LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) child.getLayoutParams();
- int gravity = lp.gravity;
- if (gravity < ) {
- gravity = minorGravity;
- }
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.CENTER_HORIZONTAL:
- childLeft = paddingLeft + ((childSpace - childWidth) / )
- + lp.leftMargin - lp.rightMargin;
- break;
- case Gravity.RIGHT:
- childLeft = childRight - childWidth - lp.rightMargin;
- break;
- case Gravity.LEFT:
- default:
- childLeft = paddingLeft + lp.leftMargin;
- break;
- }
- if (hasDividerBeforeChildAt(i)) {
- childTop += mDividerHeight;
- }
- childTop += lp.topMargin;
- setChildFrame(child, childLeft, childTop + getLocationOffset(child),
- childWidth, childHeight);
- childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
- i += getChildrenSkipCount(child, i);
- }
- }
- }
这个方法会遍历子元素并调用setChildFrame()方法:
- private void setChildFrame(View child, int left, int top, int width, int height) {
- child.layout(left, top, left + width, top + height);
- }
在setChildFrame()方法中调用子元素的layout()方法来确定自己的位置。我们看到childTop这个值是逐渐增大的,这是为了在垂直方向,子元素是一个接一个排列的而不是重叠的。
2.View的draw流程
View的draw流程很简单,先来看看View的draw()方法:
- public void draw(Canvas canvas) {
- final int privateFlags = mPrivateFlags;
- final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
- (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
- mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
- // Step 1, draw the background, if needed
- int saveCount;
- if (!dirtyOpaque) {
- drawBackground(canvas);
- }
- ...
- // Step 2, save the canvas' layers
- int paddingLeft = mPaddingLeft;
- final boolean offsetRequired = isPaddingOffsetRequired();
- if (offsetRequired) {
- paddingLeft += getLeftPaddingOffset();
- }
- ...
- // Step 3, draw the content
- if (!dirtyOpaque) onDraw(canvas);
- // Step 4, draw the children
- dispatchDraw(canvas);
- ...
- // Step 5, draw the fade effect and restore layers
- final Paint p = scrollabilityCache.paint;
- final Matrix matrix = scrollabilityCache.matrix;
- final Shader fade = scrollabilityCache.shader;
- ...
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
- if (mOverlay != null && !mOverlay.isEmpty()) {
- mOverlay.getOverlayView().dispatchDraw(canvas);
- }
- }
从源码的注释我们看到draw流程有六个步骤,其中第2步和第5步可以跳过:
- 如果有设置背景,则绘制背景
- 保存canvas层
- 绘制自身内容
- 如果有子元素则绘制子元素
- 绘制效果
- 绘制装饰品(scrollbars)
好了,关于View的工作流程就讲到这里了,接下来会讲到自定义View。
Android View体系(八)从源码解析View的layout和draw流程的更多相关文章
- Android IntentService使用介绍以及源码解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...
- Alink漫谈(十八) :源码解析 之 多列字符串编码MultiStringIndexer
Alink漫谈(十八) :源码解析 之 多列字符串编码MultiStringIndexer 目录 Alink漫谈(十八) :源码解析 之 多列字符串编码MultiStringIndexer 0x00 ...
- Netty 源码解析(七): NioEventLoop 工作流程
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- 【Android应用开发】EasyDialog 源码解析
示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...
- Android Handler机制(四)---Handler源码解析
Handler的主要用途有两个:(1).在将来的某个时刻执行消息或一个runnable,(2)把消息发送到消息队列. 主要依靠post(Runnable).postAtTime(Runnable, l ...
- Android Handler机制(三)----Looper源码解析
一.Looper Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message. Class used to run a message loop for a ...
- Android Handler机制(二)---MessageQueue源码解析
MessageQueue 1.变量 private final boolean mQuitAllowed;//表示MessageQueue是否允许退出 @SuppressWarnings(" ...
- tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解
TP6源码解析,ThinkPHP6模板编译流程详解 前言:刚开始写博客.如果觉得本篇文章对您有所帮助.点个赞再走也不迟 模板编译流程,大概是: 先获取到View类实例(依赖注入也好,通过助手函数也好) ...
- Spring Security源码解析一:UsernamePasswordAuthenticationFilter之登录流程
一.前言 spring security安全框架作为spring系列组件中的一个,被广泛的运用在各项目中,那么spring security在程序中的工作流程是个什么样的呢,它是如何进行一系列的鉴权和 ...
随机推荐
- Qt之实现360安全卫士主界面代码开源
匆匆一年又过去了,总结去年一年的节奏就是忙爆了:生活忙.工作忙,值得庆幸的是没有瞎忙:今天打开博客园查看我的博客,才发现几乎差不多一年时间没写博客了:博客文章就是记忆,就是曾经努力过的见证,感谢博客园 ...
- Scala + IntelliJ IDEA
学习路上的新起点:大数据Scala + Spark +(HDFS + HBase),本文主要介绍下Scala的基本语法和用法吧.最后再简单介绍一种Java开发工具IntelliJ IDEA的使用. S ...
- 简介 - RESTful
RESTful REST(Representational State Transfer,表现层状态转化),可以简单理解为"资源在网络中以某种表现形式进行状态转移" Resourc ...
- Testing - 软件测试知识梳理 - 测试用例
测试用例 是指对一项特定的软件产品进行测试任务的描述,体现测试方案.方法.技术和策略. 内容包括测试目标.测试环境.输入数据.测试步骤.预期结果.测试脚本等,并形成文档. 每个具体测试用例都将包括下列 ...
- javascript 实现数据结构 - 队列
队列是遵循FIFO(First In First Out,先进先出,也称为先来先服务)原则的一组有序的项.队列在尾部添加新元素,并从顶部移除元素.最新添加的元素必须排在队列的末尾. 1.构造函数构建队 ...
- python之线程(threading)
线程是属于进程的,一个进程可能包含多个线程 至于线程和进程在使用时哪个更好,只能看使用的场景了 话不多说,看下线程模块(threading)的使用方法: #导入模块 import threading, ...
- ubuntu18.04 运行时提示缺少libstdc++.so.6
解决方法:输入命令 sudo apt- 提示:ubuntu默认软件包管理器不是yum,而是dpkg,安装软件时用apt-get PS:在ubuntu下最好不要去装yum,不然可能会出现一些奇怪的问题
- Ubuntu 16.04下如何安装VMware-Workstation
一.下载 下载地址:https://my.vmware.com/cn/group/vmware/details?downloadGroup=WKST-1411-LX&productId=686 ...
- QC内部分享ppt
Quality Center是一个基于Web的测试管理工具,可以组织和管理应用程序测试流程的所有阶段,包括制定测试需求.计划测试.执行测试和跟踪缺陷.此外,通过Quality Center还可以创建报 ...
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十六):容器部署项目
容器部署项目 这一章我们引入docker,采用docker容器的方式部署我们的项目. 首先需要有一个linux环境,并且安装 java 和 maven 以及 docker 环境,这个教程多如牛毛,不再 ...