Android LinearLayout,RelativeLayout,FrameLayout onlayout()方法介绍

摘要

很多大神都介绍过view得绘制过程,但是对onLayout()方法没有具体介绍,我们这里来简单介绍一下,让大家对其有个基本的了解

view的绘制过程

先用时序图简单介绍一下view的绘制过程.
Android LinearLayout,RelativeLayout,FrameLayout onlayout()方法介绍_第1张图片

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 params = (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开发,android,Widget)