Android LinearLayout,RelativeLayout,FrameLayout onlayout()方法介绍
2015-11-12 10:28
701 查看
摘要
很多大神都介绍过view得绘制过程,但是对onLayout()方法没有具体介绍,我们这里来简单介绍一下,让大家对其有个基本的了解view的绘制过程
先用时序图简单介绍一下view的绘制过程.ViewRoot会依次调用mView的onMeasure(),onLayout(),onDraw()方法。如果mView是ViewGroup的子类,比如说大家经常用的LinearLayout,RelativeLayout等,则会调用子view相对应的onXXX方法,完成绘制。
简单介绍一下viewroot。源码实现上来看,ViewRoot和View对象并没有任何“血缘”关系,它既非View的子类,也非View的父类。ViewRoot可以被理解为“View树的管理者”——它有一个mView成员变量,指向的是它所管理的View树的根。
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); } }
onLayout方法中,针对orientation做了不同的处理,我们下面以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) / 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); } } }
1.line 2-12 , 主要是是计算子view的水平方向上的一些值,如childSpace——用于在子view的layout_gravity=left的时候计算childleft,childRight——用于子view的layout_gravity=right的时候计算。
2.line 19-34, 根据gravity的值,来计算子view的childTop。这里的代码也告诉我们,如果viewgroup的方向是vertical,那么子view的layout_gravity设置为top,bottom等垂直方向上的一些值时,是没有意义的。
3.line 36-80,计算子view的layout需要的l,r,t,b值。line53-67针对子view的layout_gravity属性,来计算子view的left值。计算完成后,利用setChildFrame触发子view的layout操作。
private void setChildFrame(View child, int left, int top, int width, int height) { child.layout(left, top, left + width, top + height); }
setChildFrame的参数中,left和top分别对应childLeft,childTop,是子view在父view中得相对位置。width和height对应于父viewgroup给子view的最大width和height,子view在进行layout时,自己的宽和高在onmeasure的时候都计算好了,这里又有父view设定的最大宽和高,需要的条件都具备了。
RelativeLayout onLayout()方法介绍
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ............................. View[] views = mSortedHorizontalChildren; int count = views.length; for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams p bf83 arams = (LayoutParams) child.getLayoutParams(); int[] rules = params.getRules(layoutDirection); applyHorizontalSizeRules(params, myWidth, rules); measureChildHorizontal(child, params, myWidth, myHeight); if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { offsetHorizontalAxis = true; } } } views = mSortedVerticalChildren; count = views.length; final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); applyVerticalSizeRules(params, myHeight); measureChild(child, params, myWidth, myHeight); if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { offsetVerticalAxis = true; } ............... } } ............. }
这里对部分方法做一下介绍。
1.line5-21,对childview进行遍历,对每一个childview进行applyHorizontalSizeRules方法,对子view的layoutParams进行赋值。
2.line27-40,对childview进行遍历,和操作1基本相同,对子view的layoutparams执行了applyVerticalSizeRules方法,我们下面只对applyHorizontalSizeRules做介绍。
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) { RelativeLayout.LayoutParams anchorParams; // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example: // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it // wants to the right // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it // wants to the left // left=10, right=20 means the left and right ends are both fixed childParams.mLeft = VALUE_NOT_SET; childParams.mRight = VALUE_NOT_SET; anchorParams = getRelatedViewParams(rules, LEFT_OF); if (anchorParams != null) { childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin + childParams.rightMargin); } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) { if (myWidth >= 0) { childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; } } ........................
子view可以设置align_toLeftOf等水平方向上的一些属性,代码里边用rules[]来存储这些约束,getRelatedViewParams(rules, LEFT_OF);就是获得子view的LEFT_OF属性依赖的view的params。此处,获得之后,计算了子view的params的right值。后续代码中,还对RIGHT_OF,ALIGN_LEFT等做了操作,这里就不一一介绍了。
看到这里,我们可以看到onMeasure里边,对子view的layoutparams的l,r,b,t都赋值了,这样,在onLayout中,就可以使用了。
FrameLayout onLayout()方法介绍
FrameLayout中的子view,相互之间没有什么约束,简单来说就是一层一层的覆盖,相对来说比较简单。@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { layoutChildren(left, top, right, bottom, false /* no force left gravity */); }
layoutChildren中,只是根据gravity对子view的top和left值做了一些计算,操作比较简单,此处就不贴代码了。
题外话——在哪看源码
给大家一个看源码的网站,http://grepcode.com/,可以搜索类名,挺方便的。本篇文章简单介绍了一下,希望对大家有用。
References
1.http://blog.csdn.net/guolin_blog/article/details/163302672.http://blog.csdn.net/yanbober/article/details/46128379
3.http://blog.csdn.net/android_jiangjun/article/details/45798221
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories