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之measure、layout、draw三大流程

自定义view之view显示流程 一个view要显示出来,需要经过测量、布局和绘制这三个过程,本章就这三个流程详细探讨一下。View的三大流程具体分析起来比较复杂,本文不会从根源详细地分析,但是可...
  • zuguorui
  • zuguorui
  • 2017年04月13日 17:39
  • 2191

Android视图的绘制流程(下)——View的Layout与Draw过程

在上篇文章中Andtoid视图的绘制流程——View的测量对View的Measure过程进行了详细的说明。对于在View的绘制的整个过程中,在对View的大小进行测量以后,便开始确定View的位置并且...
  • ljd2038
  • ljd2038
  • 2016年06月11日 13:26
  • 7156

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

一、android中view的layout过程总概 Layout过程其实就是父视图按照子视图的大小及布局参数将子视图放在窗口的合适的位置上。 视图的布局过程是从ViewRoot对象调用根视图的la...
  • ff20081528
  • ff20081528
  • 2014年01月03日 10:40
  • 21366

Android View绘制:measure,layout和draw

Android的View显示在界面上需要三步:测量,安放和绘制。
  • SummerInnPhuket
  • SummerInnPhuket
  • 2016年01月07日 18:58
  • 1574

Android进阶系列0—View的工作流程:measure,layout,draw小结

本文主要来自《Android开发艺术探索》和郭霖 《Android视图绘制流程完全解析,带你一步步深入了解View(二)》 两部分的综合理解。 本文只阐述流程,不讲述细节,大家可以结合上述两部分的细...
  • u011026779
  • u011026779
  • 2016年08月05日 19:49
  • 344

Android View深入学习(二),View的布局(Layout)过程

View布局最开始是从DecorView开始的,在ViewRootImpl中的performTraversals方法中,调用了 performLayout(lp, desiredWindowWidth...
  • jiangxuqaz
  • jiangxuqaz
  • 2015年07月18日 18:31
  • 738

9.view 作图过程,讲讲draw/onDraw和drawChild

9.view 绘制过程,讲讲draw/onDraw和drawChild 转载请标明出处:  http://blog.csdn.net/yujun411522/article/detai...
  • sd19871122
  • sd19871122
  • 2016年07月30日 16:33
  • 612

Android View体系(八)从源码解析View的layout和draw流程

上一篇文章我们讲了View的measure的流程,接下来我们讲下View的layout和draw流程,如果你理解了View的measure的流程,那这篇文章自然就不在话下了。...
  • itachi85
  • itachi85
  • 2016年03月25日 17:31
  • 4750

从源码角度分析view的draw过程

前面已经分析了Android view的测量和布局,并且重点介绍了linearLayout的测量机制。特别感谢博乐对我前一篇文章的推荐以及赞赏,我也会一如既往的写好每一篇文章。接下来的这篇文章会以前两...
  • andywuchuanlong
  • andywuchuanlong
  • 2015年08月21日 10:36
  • 1736

Android Tool——Hierachy Viewer可视化调试

引言:在Android的SDK工具包中,有很多十分有用的工具,可以帮助程序员开发和测试Android应用程序,大大提高其工作效率。其中的一款叫Hierachy Viewer的可视化调试工具,可以很方便...
  • CrazyMo_
  • CrazyMo_
  • 2016年02月23日 14:53
  • 598
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:View的工作原理(三)--View的Layout和Draw过程
举报原因:
原因补充:

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