版权声明

  • 本文原创作者:谷哥的小弟
  • 作者博客地址:http://blog.csdn.net/lfdfhl

在经过measure阶段以后,系统确定了View的测量大小,接下来就进入到layout的过程。在该过程中会确定视图的显示位置,即子View在其父控件中的位置。

layout( )源码剖析

嗯哼,我们直接扒开源码从View的layout( )开始入手。

//l, t, r, b分别表示子View相对于父View的左、上、右、下的坐标
public void layout(int l, int t, int r, int b) {if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {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 = 0; 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;}

在该方法中的主要实现
1 确定该View在其父View中的位置,请参见代码第13-14行。
在该处调用setFrame()方法,在该方法中把l,t, r, b分别与之前的mLeft,mTop,mRight,mBottom一一作比较,假若其中任意一个值发生了变化,那么就判定该View的位置发生了变化
2 若View的位置发生了变化则调用onLayout()方法,请参见代码第17行

嗯哼,我们就顺着这个思路去看看onLayout()的源码

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

额,View的onLayout()方法竟然是一个空方法!这是为啥呢?
先瞅瞅官方文档对该方法的介绍:

Called from layout when this view should assign a size and position to each of its children.

噢,原来文档中说了:在layout方法中调用该onLayout()用于指定子View的大小和位置。
谁才有子View呢?用你的小脑袋瓜想想。
哇哈,当然是ViewGroup!
这也就是说:ViewGroup会调用onLayout()决定子View的显示位置。

好吧,既然如此就去看ViewGroup中的onLayout()方法是怎么实现的;嗯哼,接着看源码

protected abstract void onLayout(boolean changed,int l, int t, int r, int b);

额,ViewGroup的onLayout()竟然是一个抽象方法!这就意味着啥呢?
这就是说ViewGroup的子类都必须重写这个方法,实现自己的逻辑。比如:FrameLayou,LinearLayout,RelativeLayout等等布局都需要重写这个方法,在该方法内依据各自的布局规则确定子View的位置。

在此以LinearLayout为例,看看ViewGroup对于onLayout()方法的实现。

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);}}

在LinearLayout的onLayout()方法中分别处理了水平线性布局和垂直线性布局。在此,就选择layoutVertical()继续往下看。

void layoutVertical(int left, int top, int right, int bottom) {final int paddingLeft = mPaddingLeft;int childTop;int childLeft;final int width = right - left;int childRight = width - mPaddingRight;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:childTop = mPaddingTop + bottom - top - mTotalLength;break;case Gravity.CENTER_VERTICAL:childTop =mPaddingTop+(bottom-top-mTotalLength) / 2;break;case Gravity.TOP:default:childTop = mPaddingTop;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();final LinearLayout.LayoutParams lp =(LinearLayout.LayoutParams) child.getLayoutParams();int gravity = lp.gravity;if (gravity < 0) {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) / 2)+ 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);}}
}

这里的逻辑不是特别简单,我们看几个重要的步骤。

第一步:

计算child可使用空间的大小,请参见代码第8行

第二步:

获取子View的个数,请参见代码第10行

第三步:

计算childTop从而确定子View的开始布局位置,请参见代码第12-28行

第四步:

确定每个子View的位置,请参见代码第30-74行
这一步是最关键的步骤,我们瞅瞅它的主要操作
1 得到子View测量后的宽和高,请参见代码第35-36行.
这里获取到的childWidth和childHeight就是在measure阶段所确立的宽和高
2 得到子View的LayoutParams,请参见代码第38-39行.
3 依据子View的LayoutParams确定子View的位置,请参见代码第41-69行.
我们可以发现在setChildFrame()中又调用了View的layout()方法来确定子View的位置。

小结:

到这我们就可以理清楚思路了:ViewGroup首先调用了layout()确定了自己本身在其父View中的位置,然后调用onLayout()确定每个子View的位置,每个子View又会调用View的layout()方法来确定自己在ViewGroup的位置。

概况地讲:

View的layout()方法用于View确定自己本身在其父View的位置
ViewGroup的onLayout()方法用于确定子View的位置

Layout源码分析与总结相关推荐

  1. android字符显示流程图,Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  2. Android应用层View绘制流程与源码分析

    前言 Activity中界面加载显示的基本流程原理,最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归 ...

  3. Django源码分析3:处理请求wsgi分析与视图View

    django源码分析 本文环境python3.5.2,django1.10.x系列 根据前上一篇runserver的博文,已经分析了本地调试服务器的大致流程,现在我们来分析一下当runserver运行 ...

  4. openmp官方源码_MNN推理过程源码分析笔记(一)主流程

    在正式开始推理代码分析之前, 回顾下 MNN整体结构 推理分为三个大部分 Engine Backends Runtime Optimize 那么问题来了,从哪里开始,怎么入手呢? 我的心得是源码分析不 ...

  5. android view 源码分析,Android ViewPager源码详细分析

    1.问题 由于Android Framework源码很庞大,所以读源码必须带着问题来读!没有问题,创造问题再来读!否则很容易迷失在无数的方法与属性之中,最后无功而返. 那么,关于ViewPager有什 ...

  6. 【Android 插件化】VirtualApp 源码分析 ( 添加应用源码分析 | LaunchpadAdapter 适配器 | 适配器添加元素 | PackageAppData 元素 )

    文章目录 一.添加应用源码分析 1.LaunchpadAdapter 适配器 2.适配器添加元素 3.PackageAppData 元素 一.添加应用源码分析 1.LaunchpadAdapter 适 ...

  7. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  8. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  9. 【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 源码分析 | 创建 Protobuf 对象 )

    文章目录 一.Protobuf 源码分析 二.创建 Protobuf 对象 三.完整代码示例 四.参考资料 一.Protobuf 源码分析 Protobuf 源文件如下 : addressbook.p ...

最新文章

  1. 当我们在讨论奢侈品行业时,人工智能可以做什么?
  2. ML 05、分类、标注与回归
  3. SystemVerilog搭建验证平台使用DPI时遇到的问题及解决方案
  4. oracle group by用法_从OceanBase TPCC测试报告看ORACLE兼容性进展
  5. layui 单独使用日期组件
  6. python自动化测试开发_基于python的selenium2自动化测试从基础到实战(Python3、selenium2、自动化测试、web测试)...
  7. 揭秘企业实现企业盈利有哪些原则?
  8. Hbase 操作工具类
  9. 课时3:小插曲之变量和字符串
  10. 开源ext2read代码走读之--“\\\\.\\PhysicalDrive0”意义?
  11. ios开发学习- 简易音乐播放器2 (基于iPhone4s屏幕尺寸)-- 歌词解析--plist文件应用--imageNamed图片加载耗内存...
  12. 建设城市之智慧灯杆网关
  13. (附源码)springboot电影院售票与管理系统 毕业设计 011449
  14. 我国首部《密码法》来了!资深律师跟我说,他是这么看的...
  15. #研发解决方案#大数据协作平台魔盒——日拱一卒,功不唐捐
  16. Ubuntu-18.04 安装 ROS 系统
  17. java wtc_java通过wtc调用tuxedo服务超时
  18. sqlMap embedded=iSinta.Beiwo.Infrastructure.Mappers.Member.MemberAddress.xml, iSinta.Beiwo.Inf
  19. 2021独立站VS跨境电商平台怎么选?
  20. 从水果连连看到两条序列比对

热门文章

  1. 用pyecharts 实现按时间线轮播数据图
  2. [POJ2942]:Knights of the Round Table(塔尖+二分图染色法)
  3. Java项目-基于SSM实现保健院管理系统
  4. 电脑软件:硬盘信息检测工具 HD Tune Pro v5.75 介绍和使用教程
  5. 【Linux】之Jumpserver堡垒机添加Windows主机资产
  6. 报错:Failed to configure a DataSource: url attribute is not specified and no embedd
  7. ubuntu安装国内版firefox
  8. VB webbrowser 控件的应用(跨域 内嵌网页元素的访问)
  9. SFD活动全球统一,为何中国变味?
  10. html图片靠右浮动 文字左侧环绕,css怎么让文字环绕图片?