View的工作原理(三)--View的Layout和Draw过程

简介: 版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/u013132758。 https://blog.csdn.net/u013132758/article/details/52382195 前言前面两篇博客介绍了View的工作原理的measure流程,这篇博客主要介绍View的layout流程。
版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/u013132758。 https://blog.csdn.net/u013132758/article/details/52382195

前言

前面两篇博客介绍了View的工作原理的measure流程,这篇博客主要介绍View的layout流程。

View的layout流程

首先我们通过下面这幅图,来理一下View的layout流程的大致流程。


通过这幅图我们知道:Layout的作用是ViewGroup用来确定子元素所在的位置,当ViewGroup确定后,它在onLayout方法中会遍历所有子元素并调用其layout方法。如果子元素是ViewGroup则会继续调用ViewGroup的onLayout方法。

View.layout

   /* *
    * @param l Left position, relative to parent
    * @param t Top position, relative to parent
    * @param r Right position, relative to parent
    * @param b Bottom position, relative to parent
    */
  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 = setFrame(l, t, r, b);//设置View的坐标位置
        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;
    }
函数中参数l、t、r、b是指view的左、上、右、底的位置,这几个参数是父视图传入的,而根视图中参数是由performTraversals()方法传入的。而这个方法中的主要方法就是setFrame()方法。

前面我们知道onLayout方法是个空方法:我们来看一下

<span style="font-size:14px;">  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }</span>
View和ViewGroup都没有实现onLayout方法。

LinerLayout 的 onLayout方法:

  @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mOrientation == VERTICAL) {
            layoutVertical();
        } else {
            layoutHorizontal();
        }
    }//这里我们只看layoutVertical()方法
  void layoutVertical() {
        final int paddingLeft = mPaddingLeft;

        int childTop;
        int childLeft;
        
        // Where right end of child should go
        final int width = mRight - mLeft;
        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 + mBottom - mTop - mTotalLength;
               break;

               // mTotalLength contains the padding already
           case Gravity.CENTER_VERTICAL:
               childTop = mPaddingTop + (mBottom - mTop - 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);
            }
        }
    }

我们从上面可以看出,layout也是一个自上而下的过程,先设置父视图位置,在循环子视图,父视图位置一定程度上决定了子视图位置。
相关文章
|
6月前
|
存储 数据库 Android开发
Android中的View显示过程
Android中的View显示过程
77 1
|
7月前
|
前端开发 Android开发 容器
自定义View之View的工作原理
自定义View之View的工作原理
60 0
|
存储 缓存 索引
RecyclerView 动画原理 | pre-layout,post-layout 与 scrap 缓存的关系
RecyclerView 动画原理 | pre-layout,post-layout 与 scrap 缓存的关系
92 0
|
API uml Android开发
Android | 深入理解View.post()获取宽高、Window加载View原理
深入理解View.post()获取宽高、Window加载View原理
444 0
|
Android开发
图+源码,读懂View的Layout方法
本篇文章就带大家学习 View 绘制三大方法的第二个方法——Layout 方法。
图+源码,读懂View的Layout方法
调用View#requestLayout后,哪些View会被影响?
调用View#requestLayout后,哪些View会被影响?
|
Android开发
从自定义ViewGroup看layout作用
废话不多说,开始今天的Android之旅~
135 0
从自定义ViewGroup看layout作用
|
前端开发 vr&ar 容器
Flutter 115: 图解自定义 View 之 Canvas (四) drawParagraph
0 基础学习 Flutter,第一百一十五节:自定义 Canvas 第四节,文本绘制小结!
676 0
Flutter 115: 图解自定义 View 之 Canvas (四) drawParagraph
|
容器
View的绘制过程
View的绘制过程从Activity.setContentView开始经过如下方法: Activity.setContentView—>PhoneWindow.
1011 0