最近在深入学习Android视图的绘制流程,从相对简单的FramgLayout开始。
onMeasure函数主要是用来计算子视图以及ViewGroup自身的大小,代码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    int count = getChildCount();    //如果width或者height的mode有一个不为EXACTLY那么使能measureMatchParentChildren    final boolean measureMatchParentChildren =            MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||            MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;    //清除mMatchParentChildren缓存,mMatchParentChildren中保存了前一次onMeasure中宽或高被设置为match_parent的view    mMatchParentChildren.clear();    int maxHeight = 0;    int maxWidth = 0;    int childState = 0;    //step1.遍历计算所有子View大小    for (int i = 0; i < count; i++) {        final View child = getChildAt(i);        //mMeasureAllChildren如果使能,那么无视可见性,强制measure所有视图,默认false        if (mMeasureAllChildren || child.getVisibility() != GONE) {            //measure子View,该函数属于ViewGroup            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            //记录子视图最大宽度            maxWidth = Math.max(maxWidth,                    child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);            //记录子视图最大高度            maxHeight = Math.max(maxHeight,                    child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);            childState = combineMeasuredStates(childState, child.getMeasuredState());            //如果measureMatchParentChildren使能那么            //向mMatchParentChildren中加入所有高或宽为Match_Parent的view            if (measureMatchParentChildren) {                if (lp.width == LayoutParams.MATCH_PARENT ||                        lp.height == LayoutParams.MATCH_PARENT) {                    mMatchParentChildren.add(child);                }            }        }    }    // Account for padding too    // 考虑padding    maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();    maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();    // Check against our minimum height and width    // 与最小宽高比较,取较大值    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());    // Check against our foreground's minimum height and width    // 与前景图片比较,获取宽高相对较大的值    final Drawable drawable = getForeground();    if (drawable != null) {        maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());        maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());    }    //step2.设置FrameLaout自身的尺寸    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),            resolveSizeAndState(maxHeight, heightMeasureSpec,                    childState << MEASURED_HEIGHT_STATE_SHIFT));    //step3.绘制宽或高为Match_Parent的view    count = mMatchParentChildren.size();    if (count > 1) {        for (int i = 0; i < count; i++) {            final View child = mMatchParentChildren.get(i);            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();            int childWidthMeasureSpec;            int childHeightMeasureSpec;            if (lp.width == LayoutParams.MATCH_PARENT) {                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -                        getPaddingLeftWithForeground() - getPaddingRightWithForeground() -                        lp.leftMargin - lp.rightMargin,                        MeasureSpec.EXACTLY);            } else {                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,                        getPaddingLeftWithForeground() + getPaddingRightWithForeground() +                        lp.leftMargin + lp.rightMargin,                        lp.width);            }            if (lp.height == LayoutParams.MATCH_PARENT) {                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -                        getPaddingTopWithForeground() - getPaddingBottomWithForeground() -                        lp.topMargin - lp.bottomMargin,                        MeasureSpec.EXACTLY);            } else {                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,                        getPaddingTopWithForeground() + getPaddingBottomWithForeground() +                        lp.topMargin + lp.bottomMargin,                        lp.height);            }            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);        }    }}// 考虑子视图外边距计算子视图大小protected void measureChildWithMargins(View child,            int parentWidthMeasureSpec, int widthUsed,            int parentHeightMeasureSpec, int heightUsed) {    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();    //根据parentWidthMeasureSpec,ViewGroup剩余宽度,child的宽度重新计算child的widthMeasureSpec    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin                    + widthUsed, lp.width);    //根据parentHeightMeasureSpec,ViewGroup剩余高度度,child的高度重新计算childHeightMeasureSpec    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin                    + heightUsed, lp.height);    //调用measure计算大小    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

onLayout计算子视图的位置:

@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {    // 直接调用了FrameLayout中的layoutChildren方法    layoutChildren(left, top, right, bottom, false /* no force left gravity */);}//该方法中的参数其实都是相对于父视图的void layoutChildren(int left, int top, int right, int bottom,                                  boolean forceLeftGravity) {    final int count = getChildCount();    // 获取各个方向的padding值    final int parentLeft = getPaddingLeftWithForeground();    final int parentRight = right - left - getPaddingRightWithForeground();    final int parentTop = getPaddingTopWithForeground();    final int parentBottom = bottom - top - getPaddingBottomWithForeground();    mForegroundBoundsChanged = true;    // 循环layout所有子View    for (int i = 0; i < count; i++) {        final View child = getChildAt(i);        // 不绘制可见性为GONE的View        if (child.getVisibility() != GONE) {            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            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;            }            // 获取布局方向,可能的值为rtl、ltr,分别为从右至左、从左至右布局            final int layoutDirection = getLayoutDirection();            // 结合layoutDirection和View的layout_gravity计算出真正的gravity            // Gravity.getAbsoluteGravity是一个静态方法            // 源码注释:            // if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.            // if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.            // 水平方向如果为从左到右,那么,Gratity为START会被设置成LEFT,END会被设置成RIGHT            // 水平方向如果为从右到左,那么,Gratity为START会被设置成RIGHT,END会被设置成LEFT            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);            // 获取垂直方向的Gravity            final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;            // 根据不同的水平比重计算View的水平坐标            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {                case Gravity.CENTER_HORIZONTAL:                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +                    lp.leftMargin - lp.rightMargin;                    break;                case Gravity.RIGHT:                    if (!forceLeftGravity) {                        childLeft = parentRight - width - lp.rightMargin;                        break;                    }                case Gravity.LEFT:                default:                    childLeft = parentLeft + lp.leftMargin;            }            //根据垂直比重计算View的垂直坐标            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;            }            child.layout(childLeft, childTop, childLeft + width, childTop + height);        }    }}

FrameLayout并没有继承onDraw方法,而是继承了draw:

// 仅仅绘制了前景图片public void draw(Canvas canvas) {    super.draw(canvas);    if (mForeground != null) {        final Drawable foreground = mForeground;        // 根据mForegroundBoundsChanged判断是否重新设置界限        // mForegroundBoundsChanged在onLayout以及onSizeChanged方法中会被使能        if (mForegroundBoundsChanged) {            mForegroundBoundsChanged = false;            final Rect selfBounds = mSelfBounds;            final Rect overlayBounds = mOverlayBounds;            final int w = mRight-mLeft;            final int h = mBottom-mTop;            // 根据mForegroundInPadding判断前景是否被padding所束缚,重新设置bounds            if (mForegroundInPadding) {                selfBounds.set(0, 0, w, h);            } else {                selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);            }            final int layoutDirection = getLayoutDirection();            Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),                    foreground.getIntrinsicHeight(), selfBounds, overlayBounds,                    layoutDirection);            foreground.setBounds(overlayBounds);        }        foreground.draw(canvas);    }}

以上。

更多相关文章

  1. Android应用TranslateAnimation移动之后,利用视图的setLayoutPara
  2. Android绚丽加载效果视图(loading)控件
  3. 阅读《Android(安卓)从入门到精通》(22)——网格视图
  4. android api 中文 (75)―― AdapterView.OnItemClickListener
  5. Android(安卓)touch 事件的处理流程
  6. Animation动画概述和执行原理
  7. Android(安卓)canvas绘图基础之运动的时钟
  8. Android函数计算器(含源码,Android测试工程)
  9. Android(安卓)最常用的快速开发工具类

随机推荐

  1. 写信息的界面
  2. 初步了解Android
  3. Android(安卓)TextView设置图片技巧
  4. Android studio build inished with non-
  5. 分享android技术牛人博客
  6. Android:解决列表滚动时背景色变黑的方法
  7. Android 文件实现断点上传
  8. ActionBar setDisplayOptions 使用详解
  9. adt-bundle-windows-x86_32-20140702
  10. Android Webview播放HTML5 video的一个思