相比较onMeasure ,layout过程要简单多了,正如layout的中文意思“布局”中表达的一样,layout的过程就是确定View在屏幕上显示的具体位置,在代码中就是设置其成员变量mLeft,mTop,mRight,mBottom的值,这几个值构成的矩形区域就是该View显示的位置,不过这里的具体位置都是相对与父视图的位置。

与onMeasure过程类似,ViewGroup在onLayout函数中通过调用其children的layout函数来设置子视图相对与父视图中的位置,具体位置由函数layout的参数决定,当我们继承ViewGroup时必须重载onLayout函数(ViewGroup中onLayout是abstract修饰),然而onMeasure并不要求必须重载,因为相对与layout来说,measure过程并不是必须的,具体后面会提到。首先我们来看下View.java中函数layout和onLayout的源码:

public void layout(int l, int t, int r, int b) {int oldL = mLeft;int oldT = mTop;int oldB = mBottom;int oldR = mRight;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;ListenerInfo li = mListenerInfo;if (li != null && li.mOnLayoutChangeListeners != null) {ArrayList<OnLayoutChangeListener> listenersCopy =(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();int numListeners = listenersCopy.size();for (int i = 0; i < numListeners; ++i) {listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);}}}mPrivateFlags &= ~FORCE_LAYOUT;}

函数layout的主体过程还是很容易理解的,首先通过调用setFrame函数来对4个成员变量(mLeft,mTop,mRight,mBottom)赋值,然后回调onLayout函数,最后回调所有注册过的listener的onLayoutChange函数。

对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数:

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}

接着我们来看下ViewGroup.java中layout的源码:

public final void layout(int l, int t, int r, int b) {if (mTransition == null || !mTransition.isChangingLayout()) {super.layout(l, t, r, b);} else {// record the fact that we noop'd it; request layout when transition finishesmLayoutSuppressed = true;}}

super.layout(l, t, r, b)调用的即是View.java中的layout函数,相比之下ViewGroup增加了LayoutTransition的处理,LayoutTransition是用于处理ViewGroup增加和删除子视图的动画效果,也就是说如果当前ViewGroup未添加LayoutTransition动画,或者LayoutTransition动画此刻并未运行,那么调用super.layout(l, t, r, b),继而调用到ViewGroup中的onLayout,否则将mLayoutSuppressed设置为true,等待动画完成时再调用requestLayout()。

      上面super.layout(l, t, r, b)会调用到ViewGroup.java中onLayout,其源码实现如下:
    @Overrideprotected abstract void onLayout(boolean changed,int l, int t, int r, int b);

和前面View.java中的onLayout实现相比,唯一的差别就是ViewGroup中多了关键字abstract的修饰,也就是说ViewGroup类只能用来被继承,无法实例化,并且其子类必须重载onLayout函数,而重载onLayout的目的就是安排其children在父视图的具体位置。重载onLayout通常做法就是起一个for循环调用每一个子视图的layout(l, t, r, b)函数,传入不同的参数l, t, r, b来确定每个子视图在父视图中的显示位置。
      那layout(l, t, r, b)中的4个参数l, t, r, b如何来确定呢?联想到之前的measure过程,measure过程的最终结果就是确定了每个视图的mMeasuredWidth和mMeasuredHeight,这两个参数可以简单理解为视图期望在屏幕上显示的宽和高,而这两个参数为layout过程提供了一个很重要的依据(但不是必须的),为了说明这个过程,我们来看下LinearLayout的layout过程:

void layoutVertical() {……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();……setChildFrame(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight);childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);i += getChildrenSkipCount(child, i);}}}
private void setChildFrame(View child, int left, int top, int width, int height) {        child.layout(left, top, left + width, top + height);}

从setChildFrame可以看到LinearLayout中的子视图的右边界等于left + width,下边界等于top+height,也就是说在LinearLayout中其子视图显示的宽和高由measure过程来决定的,因此measure过程的意义就是为layout过程提供视图显示范围的参考值。

      layout过程必须要依靠measure计算出来的mMeasuredWidth和mMeasuredHeight来决定视图的显示大小吗?事实并非如此,layout过程中的4个参数l, t, r, b完全可以由视图设计者任意指定,而最终视图的布局位置和大小完全由这4个参数决定,measure过程得到的mMeasuredWidth和mMeasuredHeight提供了视图大小的值,但我们完全可以不使用这两个值,可见measure过程并不是必须的。\\
      说到这里就不得不提getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()这两对函数之间的区别,getMeasuredWidth()、getMeasuredHeight()返回的是measure过程得到的mMeasuredWidth和mMeasuredHeight的值,而getWidth()和getHeight()返回的是mRight - mLeft和mBottom - mTop的值,看View.java中的源码便一清二楚了:
public final int getMeasuredWidth() {return mMeasuredWidth & MEASURED_SIZE_MASK;}
public final int getWidth() {return mRight - mLeft;}

这也解释了为什么有些情况下getWidth()和getMeasuredWidth()以及getHeight()和getMeasuredHeight()会得到不同的值。

 
      总结:整个layout过程比较容易理解,一般情况下layout过程会参考measure过程中计算得到的mMeasuredWidth和mMeasuredHeight来安排子视图在父视图中显示的位置,但这不是必须的,measure过程得到的结果可能完全没有实际用处,特别是对于一些自定义的ViewGroup,其子视图的个数、位置和大小都是固定的,这时候我们可以忽略整个measure过程,只在layout函数中传入的4个参数来安排每个子视图的具体位置。

转载于:https://www.cnblogs.com/xilinch/archive/2012/10/24/2737248.html

Android中layout过程详解相关推荐

  1. Android中layout过程详解 (结合Android 4.0.4 最新源码)

    上一篇文章Android中mesure过程详解 (结合Android 4.0.4 最新源码)介绍了View树的measure过程,相对与measure过程,本文介绍的layout过程要简单多了,正如l ...

  2. Android中mesure过程详解 (结合Android 4.0.4 最新源码)

    如何遍历并绘制View树?之前的文章Android中invalidate() 函数详解(结合Android 4.0.4 最新源码)中提到invalidate()最后会发起一个View树遍历的请求,并通 ...

  3. Android中mesure过程详解

    2019独角兽企业重金招聘Python工程师标准>>> invalidate()最后会发起一个View树遍历的请求,并通过执行performTraersal()来响应该请 求,per ...

  4. android 按键用户点击事件,Android按键事件处理过程详解

    Android按键事件处理过程详解 (2013-09-26 14:05:19) 标签: it 在Android系统中,存在多种界面事件,如点击事件.触摸事件.焦点事件和菜单事件等,在这些界面事件发生时 ...

  5. Android 中malloc_debug 原理详解

    版本基于:Android R 关联博文: Android 中malloc_debug 使用详解 0. 前言 最近上项目中遇到一个native 可能内存泄漏的问题,曾考虑使用HWASAN,但这个工具是针 ...

  6. android调webview的方法,Android中的WebView详解

    Android中的WebView详解 WebView详解 基本用法 布局文件配置WebView android:id="@+id/wv_news_detail" android:l ...

  7. Android中shape属性详解

    一.简单使用 刚开始,就先不讲一堆标签的意义及用法,先简单看看shape标签怎么用. 1.新建shape文件 首先在res/drawable文件夹下,新建一个文件,命名为:shape_radius.x ...

  8. 【Android】 Android中Log调试详解

    LOG类: public final class Log extends Object java.lang.Object   android.util.Log Constants int ASSERT ...

  9. Android中SQLite应用详解(转)

    上次我向大家介绍了SQLite的基本信息和使用过程,相信朋友们对SQLite已经有所了解了,那今天呢,我就和大家分享一下在Android中如何使用SQLite. 现在的主流移动设备像Android.i ...

最新文章

  1. 三星samsung手机ROM制作教程-另外一篇
  2. html函数属性的赋予,你可以将javascript函数名称设置为html属性吗?
  3. docker的安全管理与TLS/LLS加密通信
  4. Java_Date_01_判断两个时间相差的天数
  5. mysql恢复授权表登录_MySQL授权用户及密码恢复设置
  6. MySQL高级知识(五)——索引分析
  7. 剑指offer——25.合并两个排序的链表
  8. 从DB-Engines看传统数据库生存状况
  9. python能做什么-Python到底能做什么?
  10. Vivado安装教程详细版
  11. 计算机分析桁架受力,结构力学教学中桁架的概念分析与实践
  12. 如何设置好看的form表单样式_HTML表格表单
  13. 【热门主题:动漫进击巨人xp主题】
  14. 2020年产品经理面试题-----产品经理面试题
  15. uwsgi 的启动、停止、重启
  16. Linux-Qt--2--调试运行终止弹窗问题-The inferior stopped because it received a signal from the Operating System
  17. 基于翻译的模型-TransE,TransH,TransR,TransD
  18. 基于Linux中的通讯录管理系统(C语言+双向循环链表+MySQL)
  19. 经典智力题:经理年龄问题
  20. 2022年4月27日 复盘计划

热门文章

  1. win2008 r2 搭建FTP服务实现上传文件
  2. python线性结构图_Python学习线路图 -蓝鸥
  3. att格式汇编指令_ATT汇编语法简介
  4. 20亿人数据再被盗取,波及40多个国家和地区
  5. CF 线段树 gcd改变
  6. html微软雅黑无效,求让所有浏览器支持微软雅黑的方法_html/css_WEB-ITnose
  7. 什么才是最好的语言?
  8. 004-python-列表、元组、字典
  9. 乌镇夜宴——程序员的江湖
  10. js调用android相册,【方法】移动端H5如何调用相册和相机上传图片、音频、视频...