到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程。只是在学习layout过程之前。大家有没有发现我换了编辑器,哈哈。最终下定决心从Html编辑器切换为markdown编辑器。这里之所以使用“下定决心”这个词。是由于毕竟Html编辑器使用好几年了。非常多习惯都已经养成了,要改变多年的习惯确实不易。相信这也是还有非常多人坚持使用Html编辑器的原因。

这也反应了一个现象。当人对某一事物非常熟悉时,一旦出现了新的事物想代替老的事物时,人们都有一种抵触的情绪,做技术的人也一样,当他对某一技术非常熟悉时,当新的同类技术出来时,都有抵触情绪,这也就是网上总有人讨论各种编程语言的好坏的原因,同一时候你会发现一个问题。当你对某种技术非常熟悉时。假设完毕某项任务使用你熟悉的技术完毕工作量非常大,而使用第二种新的技术却非常easy实现,相信大部分都会选择熟悉的技术实现,即使他的工作量非常大。正如html编辑器和markdown编辑器一样。markdown在排版方面明显比html编辑器强大,可是还有非常多人不愿意切换过来。可是我今天想说的事实上是作为程序猿,我们的领域假设出现了新的技术,在我们有精力的前提下我们还是应该花时间去研究,至少不应该有种抵触的情绪。好吧,今天扯淡就到这里吧….
如今就開始学习ViewGroup的layout过程吧,假设你还没有学习过我前面的文章。建议先去阅读前面两篇相关文章
Android 中View的绘制机制源代码分析一
Android 中View的绘制机制源代码分析二
如同measure方法一样,layout方法也是从ViewRoot类的performTraversals方法调用,代码例如以下:

 final boolean didLayout = mLayoutRequested;boolean triggerGlobalLayoutListener = didLayout|| attachInfo.mRecomputeGlobalAttributes;if (didLayout) {mLayoutRequested = false;mScrollMayChange = true;if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v("ViewRoot", "Laying out " + host + " to (" +host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");long startTime = 0L;if (Config.DEBUG && ViewDebug.profileLayout) {startTime = SystemClock.elapsedRealtime();}host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) {throw new IllegalStateException("The view hierarchy is an inconsistent state,"+ "please refer to the logs with the tag "+ ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation.");}}

我们发现调用的就是host.layout(0,0,host.mMeasureWidth,host.mMeasureHeight),在前面的文章中已经说过host就是DecorView。host.mMeasuredWidth和host.mMeasureHeight经过了measure过程后分别就是host的宽度和高度,事实上也就是屏幕的宽度和高度。layout方法是View中的一个方法。我们先看看layout的代码吧

    /**** @param l Left position, relative to parent* @param t Top position, relative to parent* @param r Right position, relative to parent* @param b Bottom position, relative to parent*/public final void layout(int l, int t, int r, int b) {boolean changed = setFrame(l, t, r, b);if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {if (ViewDebug.TRACE_HIERARCHY) {ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);}onLayout(changed, l, t, r, b);mPrivateFlags &= ~LAYOUT_REQUIRED;}mPrivateFlags &= ~FORCE_LAYOUT;}

layout和measure一样,是一个final方法,所以子类无法改变它的行为,在layout中主要调用onLayout方法完毕实际的逻辑,可是并非每次laout方法都会调用onLayout方法的,首先会调用setFrame方法将上下左右的位置分别保存起来,而且在setFrame方法中会推断和上次的上下左右的位置是否一样,假设不一样保存起来并返回true,否则直接返还false.仅仅有返还true或者有LAYOUT_REQUIRED标记才会调用onLayout方法,而onLayout方法须要子类(ViewGroup)自己去依据自己的情况实现,所以在自己定义ViewGroup时,常常须要改写onLayout。在onLayout里面我们能够依据自己的需求在布局View在ViewGroup的摆放位置。至于layout的四个參数凝视里面已经写清楚了。分别代表View 左边,顶部,右边,底部在父视图中的位置,通过上面传入的參数,能够知道host在屏幕中是满屏的。为了对layout有更深入的理解,我这里使用LinearLayout解说怎样利用layout进行子View的位置分配。

    @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (mOrientation == VERTICAL) {layoutVertical();} else {layoutHorizontal();}}

在LinearLayout的onLayout方法中,和onMeasure方法一样,依据当前LinearLayout的排列方式分别调用layoutVertical和LayoutHorizontal,这里我们还是看看竖排的 layoutVertical吧

    /*** Position the children during a layout pass if the orientation of this* LinearLayout is set to {@link #VERTICAL}.** @see #getOrientation()* @see #setOrientation(int)* @see #onLayout(boolean, int, int, int, int)*/void layoutVertical() {//距离左边的距离final int paddingLeft = mPaddingLeft;//child的顶部,默认情况等于顶部padingint childTop = mPaddingTop;int childLeft;// LinearLayout可用宽度final int width = mRight - mLeft;int childRight = width - mPaddingRight;// Space available for childint childSpace = width - paddingLeft - mPaddingRight;//子View的个数final int count = getVirtualChildCount();final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;//依据LinearLayotu的gravity的值计算childTop的位置if (majorGravity != Gravity.TOP) {switch (majorGravity) {case Gravity.BOTTOM:// mTotalLength contains the padding already, we add the top// padding to compensatechildTop = mBottom - mTop + mPaddingTop - mTotalLength;break;case Gravity.CENTER_VERTICAL:childTop += ((mBottom - mTop)  - mTotalLength) / 2;break;}}for (int i = 0; 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();//拿到子View的LayoutParamsfinal LinearLayout.LayoutParams lp =(LinearLayout.LayoutParams) child.getLayoutParams();int gravity = lp.gravity;if (gravity < 0) {gravity = minorGravity;}//计算子View在水平方向的childLeftswitch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {case Gravity.LEFT:childLeft = paddingLeft + lp.leftMargin;break;case Gravity.CENTER_HORIZONTAL:childLeft = paddingLeft + ((childSpace - childWidth) / 2)+ lp.leftMargin - lp.rightMargin;break;case Gravity.RIGHT:childLeft = childRight - childWidth - lp.rightMargin;break;default:childLeft = paddingLeft;break;}childTop += lp.topMargin;setChildFrame(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight);childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);i += getChildrenSkipCount(child, i);}}}

事实上LinearyLayout的layoutVertical方法的逻辑非常easy:首先计算子View在LinearLayout中的起始位置。也就是上面的childTop,就算时首先推断当前LinearLayout在垂直方向上的 对齐方式:
1. 假设是Gravity.Bottom,那么childTop = mBottom - mTop + mPaddingTop - mTotalLength; 这个非常好理解,所以假设mTotalLenght比屏幕的高度大时,childTop非常有可能是负值。从而顶部看不见
2. 假设是Gravity.CENTER_VERTICAL。那么childTop += ((mBottom - mTop) - mTotalLength) / 2;

3.假设是Gravity.Top 那么childTop = mPaddingTop; 这样的是默认值 三种对齐方式相应的效果图例如以下:

childTop计算完毕后開始遍历各个子View,依据LinearLayout的水平方向的布局计算childLeft,这里可能有些人就犯糊涂了。这里是垂直布局,为什么要看水平方向,由于即使垂直方向,子视图也能够水平居中,所以不同的水平布局算childLeft是不一样的。

假设是Gravity.LEFT 那么childLeft = paddingLeft + lp.leftMargin;
假设是Gravity.CENTER_HORIZONTAL 那么childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp.leftMargin - lp.rightMargin;
假设是Gravity.RIGHT 那么是
childLeft = childRight - childWidth - lp.rightMargin;`

如今childTop和childLeft都计算好了,由于已经measure过。所以childBottom和childRight非常easy算出,这里调用了setChildFrame方法,该方法实际就是调用child.layout方法设置child的布局位置。

至此,LinearLayout的布局过程已经解说完毕。

Android 中View的绘制机制源代码分析 三相关推荐

  1. Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解...

    2019独角兽企业重金招聘Python工程师标准>>> 今天继续给大家分享下View的相关知识,重点有一下两点:   1.View的几种不同状态属性            2.如何根 ...

  2. 从源码解析-Android中View的绘制流程及performTraversals方法

    谈谈Activity的setContentView是怎么加载XML视图的 谈谈Activity的View怎么与View绘制工具ViewRootImpl关联的 在前面两篇文章中分析了View是如何跟绘制 ...

  3. 精通Android自定义View(十)绘制篇Canvas分析之绘制Path

    1 Path常用方法简析 Path在2D绘图中是一个很重要的类. Path在这里可以绘制基本的图形,也可以绘制其他复杂的图形. 2 常用API解析与示例 2.1 xxxTo方法 Path类中提供了一套 ...

  4. 精通Android自定义View(九)绘制篇Canvas分析之绘制图片

    绘制图片分为:绘制矢量图(drawPicture)和 绘制位图(drawBitmap) 1 drawBitmap 1.1 基本的绘制图片方法 //Bitmap:图片对象,left:偏移左边的位置,to ...

  5. 精通Android自定义View(十一)绘制篇Canvas分析之裁剪

    clipRect(int left, int top, int right, int bottom)  这个方法作用就是裁切一个矩形出来,但是图形不还是在canvas上面的,所以本质上还是裁切的can ...

  6. 精通Android自定义View(八)绘制篇Canvas分析之绘制文本

    1 简述 绘制文字分为三种应用场景: 情况1:指定文本开始的位置 即指定文本基线位置 基线x默认在字符串左侧,基线y默认在字符串下方 情况2:指定每个文字的位置 情况3:指定路径,并根据路径绘制文字 ...

  7. android view 绘制过程,深入理解Android中View绘制的三大流程

    前言 最近对Android中View的绘制机制有了一些新的认识,所以想记录下来并分享给大家.View的工作流程主要是指measure.layout.draw这三大流程,即测量.布局和绘制,其中meas ...

  8. Android中View绘制流程以及invalidate()等相关方法分析

                                                                                                        ...

  9. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder ...

最新文章

  1. 系统架构的过程 浮现式设计
  2. PGA Usage Larger than PGA_AGGREGATE_TARGET setting?
  3. 阿里异地多活与同城双活的架构演进
  4. 如何转移主机之间Docker镜像
  5. Winform巧用窗体设计完成弹窗数值绑定-以重命名弹窗为例
  6. python matlab大数据,Python第八课:Python数据分析基础
  7. SpringCloud微服务全栈实战讲解 - 公开课笔记
  8. file.encoding到底指的是什么呢?
  9. 数量queuepoj1149 PIGS
  10. 手把手教你如何用 TensorFlow 实现基于 DNN 的文本分类
  11. 计算机辅助设计cad综述,控制系统计算机辅助设计综述
  12. 树莓派 ubuntu gpio_如何给树莓派安装操作系统
  13. Spring父子上下文(WebApplicationContext)(防止事务失效)
  14. 关于 JVM 内存的 N 个问题(转)
  15. 信息资源管理 笔记整理
  16. Java代理模式——CGLIB动态代理
  17. 微信小程序 之修改switch组件尺寸大小
  18. 平安人寿优+计划广纳英才,平安代理人实现职涯发展
  19. CSU - 1256 天朝的单行道
  20. java求三角形周长 面积_计算三角形的周长和面积 java

热门文章

  1. spring根据名称获取bean_带你从零开始手写 spring ioc 框架,深入学习 spring 源码
  2. python第一周小测验_Python小测试
  3. python3基础知识点总结_python基础知识点总结
  4. python实现gauss-seidel迭代公式_python实现高斯(Gauss)迭代法的例子
  5. @aspect注解类不生效_springboot:@Transactional注解 VS @Service注解
  6. Statement接口实现查询数据、添加数据
  7. Linux 常用的压缩与解压缩命令详解
  8. Linux中rsync备份数据使用实例
  9. 主角有智能芯片的种田小说_推荐3本克苏鲁类小说,压抑邪恶与搞笑逗乐并存,看看是你的菜吗...
  10. PTA团体程序设计天梯赛-L2-010 排座位