Android - View的绘制流程一(measure)一文中提到,view绘制的核心逻辑都在ViewRoot的performTraversals()方法中,主要分为三个阶段:第一个阶段是measure,第二个阶段是layout,第三个阶段是draw ViewRoot类的performTraversals方法中layout方法的调用,代码如下: private void performTraversals() measure过程结束后,从这里开始layout,同样host是一个DecorView对象。host.mMeasuredWidth 和 host.mMeasuredHeight则是 measure过程结束之后得到的host的宽和高。 在较低的版本中,定义在view中的layout方法是被final修饰的,不能被复写,本博文参考的是android-19代码,layout没有被final修饰,不过不影响大致流程的理解: public void 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); protected booleansetFrame(int left, int top, int right, int bottom) { boolean changed = false; if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true; mLeft = left; mTop = top; mRight = right; mBottom = bottom; } return changed; } if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; } } layout方法的参数由父类提供,参数指定了该子视图在父视图中的左、上、右、下的位置,主要逻辑如下: 1、调用setFrame方法,先比对这些参数是否和原来的相同,如果相同,则什么都不做,只要四个参数中有一个发生改变,则将这些参数赋值给view的成员变量(mLeft、mTop、mRight和mBottom)。 2、调用onLayout方法。 // View 中的onLayout方法的定义: protected void onLayout(boolean changed, int left, int top, int right, int bottom) { } // ViewGroup 中的onLayout方法的定义: protected abstract void onLayout(boolean changed,int l, int t, int r, int b); View 中的onLayout方法默认什么都不做,ViewGroup中的onLayout方法则是一个抽象方法,所以,ViewGroup类型的视图都会在自己类中对子视图进行位置计算。 Android - View的绘制流程一(measure)一样,也从MyCustomLinearLayoutA为例,从onLayout()方法开始分析一个ViewGroup类型的视图遍历—layout子视图的详细过程。 LinearLayout类中的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的布局方向调用layoutVerticallayoutHorizontal,下面以Android - View的绘制流程一(measure)demo中MyCustomLinearLayoutA为例分析竖直方向的layoutVertical LinearLayout的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 // LinearLayout可用的宽度 final int width = right - left; // child最多能够到达的右边位置 int childRight = width - mPaddingRight; // Space available for child // 计算child的实际可用空间 int childSpace = width - paddingLeft - mPaddingRight; // 计算子view的个数 final int count = getVirtualChildCount(); final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; // 首先根据gravity的值计算出child的top值 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; } // 然后进行遍历,根据child的水平布局方式(水平居中、靠左或靠右)计算出child的left值 for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); if (child == null) { childTop += measureNullChild(i); } else if (child.getVisibility() != GONE) { // child的宽度和高度已经在measure步骤中计算过了,现在直接获取 final intchildWidth= child.getMeasuredWidth(); final intchildHeight= child.getMeasuredHeight(); // 获取child的布局参数 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; //计算完child的top和left值,获取到measure步骤中计算的宽和长,再调用setChildFrame方法设置child的位置 setChildFrame(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight); private void setChildFrame(View child, int left, int top,int width, int height) { // setChildFrame方法中调用的就是layout方法 child.layout(left, top,left + width, top + height); } childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child, i); } } } 至此,view的layout过程就分析完成了。 layout过程的核心就是根据xml文件中的gravity属性计算出top和left的值,再利用第一步 — measure所得到的宽和高的值计算出view对象的坐标(left、top、right和bottom)。 大小和位置都确定了,接下来是第三步 —draw。

更多相关文章

  1. (20120731)android面试总结(003)
  2. Android(安卓)ISurface PostBuffer 处理流程
  3. Android下在onCreate方法中获取TextView的高度
  4. 新版Cordova(>4.0)编译Android(安卓)APK打上签名方法
  5. Volley二次封装,实现网络请求缓存
  6. Android(安卓)ListView/ListActivity点击长按事件
  7. Windows下,Ant自动化编译Android项目具体步骤和方法
  8. Android(安卓)onPause和onSaveInstanceState的区别
  9. Android(安卓)2.2 API Demos -- Intents Chooser

随机推荐

  1. 阿里巴巴人脸离线活体识别Android
  2. Android手机gdb调试
  3. 《第一行代码》第十二章引用toolbar出错,
  4. 从android studio安装配置,到写出第一个小
  5. Android---添加结构线或边框的几种方式
  6. android九种对话框
  7. Android(安卓)Handler 教程
  8. Android(安卓)9.0——ActivityManagerSer
  9. make_ext4fs 文件权限控制
  10. AppWidget运用实例