您的位置:首页 > 移动开发 > Android开发

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/16330267

2.http://blog.csdn.net/yanbober/article/details/46128379

3.http://blog.csdn.net/android_jiangjun/article/details/45798221
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android view绘制