Layout源码分析与总结
版权声明
- 本文原创作者:谷哥的小弟
- 作者博客地址: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源码分析与总结相关推荐
- android字符显示流程图,Android应用层View绘制流程与源码分析
1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...
- Android应用层View绘制流程与源码分析
前言 Activity中界面加载显示的基本流程原理,最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归 ...
- Django源码分析3:处理请求wsgi分析与视图View
django源码分析 本文环境python3.5.2,django1.10.x系列 根据前上一篇runserver的博文,已经分析了本地调试服务器的大致流程,现在我们来分析一下当runserver运行 ...
- openmp官方源码_MNN推理过程源码分析笔记(一)主流程
在正式开始推理代码分析之前, 回顾下 MNN整体结构 推理分为三个大部分 Engine Backends Runtime Optimize 那么问题来了,从哪里开始,怎么入手呢? 我的心得是源码分析不 ...
- android view 源码分析,Android ViewPager源码详细分析
1.问题 由于Android Framework源码很庞大,所以读源码必须带着问题来读!没有问题,创造问题再来读!否则很容易迷失在无数的方法与属性之中,最后无功而返. 那么,关于ViewPager有什 ...
- 【Android 插件化】VirtualApp 源码分析 ( 添加应用源码分析 | LaunchpadAdapter 适配器 | 适配器添加元素 | PackageAppData 元素 )
文章目录 一.添加应用源码分析 1.LaunchpadAdapter 适配器 2.适配器添加元素 3.PackageAppData 元素 一.添加应用源码分析 1.LaunchpadAdapter 适 ...
- 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )
Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...
- 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )
Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...
- 【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 源码分析 | 创建 Protobuf 对象 )
文章目录 一.Protobuf 源码分析 二.创建 Protobuf 对象 三.完整代码示例 四.参考资料 一.Protobuf 源码分析 Protobuf 源文件如下 : addressbook.p ...
最新文章
- 当我们在讨论奢侈品行业时,人工智能可以做什么?
- ML 05、分类、标注与回归
- SystemVerilog搭建验证平台使用DPI时遇到的问题及解决方案
- oracle group by用法_从OceanBase TPCC测试报告看ORACLE兼容性进展
- layui 单独使用日期组件
- python自动化测试开发_基于python的selenium2自动化测试从基础到实战(Python3、selenium2、自动化测试、web测试)...
- 揭秘企业实现企业盈利有哪些原则?
- Hbase 操作工具类
- 课时3:小插曲之变量和字符串
- 开源ext2read代码走读之--“\\\\.\\PhysicalDrive0”意义?
- ios开发学习- 简易音乐播放器2 (基于iPhone4s屏幕尺寸)-- 歌词解析--plist文件应用--imageNamed图片加载耗内存...
- 建设城市之智慧灯杆网关
- (附源码)springboot电影院售票与管理系统 毕业设计 011449
- 我国首部《密码法》来了!资深律师跟我说,他是这么看的...
- #研发解决方案#大数据协作平台魔盒——日拱一卒,功不唐捐
- Ubuntu-18.04 安装 ROS 系统
- java wtc_java通过wtc调用tuxedo服务超时
- sqlMap embedded=iSinta.Beiwo.Infrastructure.Mappers.Member.MemberAddress.xml, iSinta.Beiwo.Inf
- 2021独立站VS跨境电商平台怎么选?
- 从水果连连看到两条序列比对
热门文章
- 用pyecharts 实现按时间线轮播数据图
- [POJ2942]:Knights of the Round Table(塔尖+二分图染色法)
- Java项目-基于SSM实现保健院管理系统
- 电脑软件:硬盘信息检测工具 HD Tune Pro v5.75 介绍和使用教程
- 【Linux】之Jumpserver堡垒机添加Windows主机资产
- 报错:Failed to configure a DataSource: url attribute is not specified and no embedd
- ubuntu安装国内版firefox
- VB webbrowser 控件的应用(跨域 内嵌网页元素的访问)
- SFD活动全球统一,为何中国变味?
- html图片靠右浮动 文字左侧环绕,css怎么让文字环绕图片?