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

【Android】View绘制过程分析之layout

2014-02-22 00:25 399 查看
续前文“【Android】View绘制过程分析之measure”,继续分析View的绘制过程。

本文分析第2阶段,分析过程的注释标记在以下代码中。

/**
* 此方法来给此View及它的子View分配大小和位置,
* 子类不要覆写此方法,而覆写onLayout(boolean, int, int, int, int)方法即可
* 左上右下4个参数,表示此View相对于父View的位置
*/
public void layout(int l, int t, int r, int b) {
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;
}

/**
* 此方法给子View分配大小和位置。
* 应该做的事情:遍历子View, 调用子View的layout(int, int, int,int)方法给子View分配大小和位置
* 左上右下4个参数,表示此View相对于父View的位置
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

/**
* View类中空实现这个方法,留给子类去实现。
* 下面看一下FrameLayout类的实现代码。
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();

/**
* 下面4个方法:
* 若不考虑padding,则以下4个值应为:
* parentLeft = 0; parentRight = right-left; parentTop = 0; parentBottom = bottom - top;
* 前面说到onLayout传入的左上右下4个参数是此View相对于父View的位置,那么right-left即求得此View所占的宽度,bottom-top即求得此View所点的高度
* 而这4个值构成的矩形范围,正是此View的占用范围
*/
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();

mForegroundBoundsChanged = true;

//遍历子View,计算子View的位置
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();

//子View的大小在onMeasure(int, int)方法中已经计算得到
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();

int childLeft;
int childTop;

int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
childLeft = parentLeft + lp.leftMargin;
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = parentRight - width - lp.rightMargin;
break;
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
//调用子View的layout(int,int,int,int), 上面计算得到的4个值确定了子View的位置范围
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
@容新华技术博客 - http://blog.csdn.net/rongxinhua - 原创文章,转载请注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐