生活随笔
收集整理的这篇文章主要介绍了
View工作原理(四)view的layout过程
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
刚过完自己的本命年,新的一年希望自己有个新的开始,祝自己在新的一年里一帆风顺,同时也祝广大的朋友们新年新气象,收获多多!
一、android中view的layout过程总概
Layout
过程其实就是父视图按照子视图的大小及布局参数将子视图放在窗口的合适的位置上。
视图的布局过程是从
ViewRoot 对象调调用根视图的 layout() 方法开始,接着 layout() 方法调用根视图的 onLayout() 方法, onLayout() 方法会对所包含的子视图逐一执行 layout操作,如果 子视图是 ViewGroup 子类对象,则继续调用子视图的 layout() ,重复这一过程。如果子视图是 View 子类对象,则在子视图重载的 onLayout() 内部只需要将自己布局到视图中,不需要对子视图进行 layout 操作,这样一次 layout 过程结束。过程如下图:
转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17784911
二、layout详细过程
View中的layout()方法源码(ViewGroup类继承了View类,layout过程先从ViewGroup子类开始):
[java] view plaincopy
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; }
a) 首先我们看这个方法的定义,用了关键字final ,说明此方法是不可被重写的,这样也就保证了 View 的 layout 过程是不变的。四个参数看注释,左、上、右、下分别相距父视图的距离。
b) 调用setFrame(l,t,r,b) 将位置保存起来,这些参数将保存到 View 内部变量 ( mLeft 、 mTop 、 mRight 、 mBottom )中。保存完变量前,会先对比这些参数是否和原来的相同,如果相同,则什么都不做,如果不同则进行重新赋值,并在赋值前给 mPrivateFlags 中添加 DRAWN 标识,同时调用 invalidate() 通知 View 系统原来占用的位置需要重绘。
c) 调用onLayout(),View 中定义的 onLayout() 方法默认什么都不做, View 系统提供 onLayout() 方法的目的是为了使系统包含的子视图的父视图能够在 onLayout() 方法对子视图进行位置分配,正因为如此,如果是父视图,则必须重写 onLayout() ,也正因为如此 ViewGroup 类才会把 onLayout 重载改成了 abstract 类型。
d)清除mPrivateFlags 中的 LAYOUT_REQUIRED 标识,因为 layout 操作已经完成。
上面提到的setFrame方法源码如下:
[java] view plaincopy
protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false ; if (DBG) { Log.d("View" , this + " View.setFrame(" + left + "," + top + "," + right + "," + bottom + ")" ); } if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true ; int drawn = mPrivateFlags & DRAWN; invalidate(); int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; mLeft = left; mTop = top; mRight = right; mBottom = bottom; mPrivateFlags |= HAS_BOUNDS; int newWidth = right - left; int newHeight = bottom - top; if (newWidth != oldWidth || newHeight != oldHeight) { onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); } if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) { mPrivateFlags |= DRAWN; invalidate(); } mPrivateFlags |= drawn; mBackgroundSizeChanged = true ; } return changed; }
View中的onLayout()方法如下:
[java] view plaincopy
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }
而ViewGroup中的onLayout()方法如下:
[java] view plaincopy
@Override protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17784911
因为ViewGroup中的onLayout()方法是一个抽象方法,所以下面我们用他的子类LinearLayout中的onLayout()方法来分析。源码如下:
onlayout()方法:
[java] view plaincopy
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mOrientation == VERTICAL) { layoutVertical(); } else { layoutHorizontal(); } }
layoutVertical()方法源码:
[java] view plaincopy
void layoutVertical() { final int paddingLeft = mPaddingLeft; int childTop = mPaddingTop; int childLeft; final int width = mRight - mLeft; 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.HORIZONTAL_GRAVITY_MASK; if (majorGravity != Gravity.TOP) { switch (majorGravity) { case Gravity.BOTTOM: childTop = 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(); final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); int gravity = lp.gravity; if (gravity < 0 ) { gravity = minorGravity; } switch (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); } } }
a) LinearLayout中的子视图有两种布局方式,一个是纵向的,一个是横向的,这里我们以纵向的分析。
b) 获得子视图的宽度。
c) 根据父视图中的grarity属性,来判断子视图的起始位置。
d) 开始for()循环,为每个子视图分配位置。对于每个子视图首先取出子视图的LayoutParams属性,并且获得gravity的值。根据gravity的值确定水平方向的起始位置,三种值分别为:LEFT,CENTER_HORIZONTAL和RIGHT.接着调用setChildFrame(),该方法内部实际上就是调用child.layout()为子视图设置布局位置。
《新程序员》:云原生和全面数字化实践 50位技术专家共同创作,文字、视频、音频交互阅读
总结
以上是生活随笔 为你收集整理的View工作原理(四)view的layout过程 的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔 网站内容还不错,欢迎将生活随笔 推荐给好友。