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

原创 2016年08月31日 13:38:12

前言

前面两篇博客介绍了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也是一个自上而下的过程,先设置父视图位置,在循环子视图,父视图位置一定程度上决定了子视图位置。
版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/u013132758。

Android的View工作原理(二)layout和draw过程

这篇文章介绍的是View三大流程中的layout和draw。因为上篇博文对mearsure和一些相关概念已经有了介绍,所以对layout和draw的学习就相对容易了。下面的图片反映的是Android系...

View的工作原理(measure、layout、draw)

View工作原理

View的工作原理之View的measure、layout、draw

View作为Android在视觉上的呈现,在Android的体系中承担着重要的作用。在我们平时的开发中,会用到各种各样的View,在有特别的需求时还要用到自定义View。所以,掌握View的底层工作原...

android-View工作原理(四)view的layout过程

本文转载自:http://blog.csdn.net/ff20081528/article/details/17784911一、android中view的layout过程总概Layout过程其实就是父...

View工作原理(三)视图大小计算过程(measure过程)

一、android视图measure过程总概 视图大小计算的过程是从根视图measure()方法开始,接着该方法会调用根视图的onMeasure()方法,onMeasure()方法会对所包含的子视图...

View工作原理(三)视图大小计算过程(measure过程)

一、android中view的measure过程总概 视图大小计算的过程是从根视图measure()方法开始,接着该方法会调用根视图的onMeasure()方法,onMeasure()方法会对所包...
  • judyge
  • judyge
  • 2016年01月13日 17:17
  • 195

View的工作原理(二)--从measure说View的测量流程

前言 上篇博客主要介绍了我们了解View工作原理前应该掌握的三个基础概念:ViewRoot,DecorVIew,MeasureSpec。这之后几篇博客主要介绍View的三大流程(Measure ,la...

安卓 View 工作流程-Measure、 Layout、Draw 源码阅读

我们在开发中都接触过 ViewRootImpl 这个类,ViewRootImpl 在 View 的绘制和事件分发都起到至关重要的作用,我们先来看看 View 的测量绘制流程是怎么传递的: 我们知道在...

View的绘制流程(三)--------view的layout和draw过程

View绘制流程三大步骤,mesasure、layout、draw 一、layout 分为两种情况,单纯的view和viewgroup view的layout流程 流程从layout方法开始 ...

view工作原理-计算视图大小的过程(onMeasure)

view的视图有两种情况: 内容型视图:由视图的内容决定其大小。 图形型视图:父视图为view动态调整大小。 ### measure的本质 把视图布局使用的“相对值”转化成具体值的过程,即把...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:View的工作原理(三)--View的Layout和Draw过程
举报原因:
原因补充:

(最多只允许输入30个字)