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也是一个自上而下的过程,先设置父视图位置,在循环子视图,父视图位置一定程度上决定了子视图位置。
相关文章
|
1月前
|
前端开发 Android开发 容器
自定义View之View的工作原理
自定义View之View的工作原理
20 0
|
11月前
|
存储 缓存 索引
RecyclerView 动画原理 | pre-layout,post-layout 与 scrap 缓存的关系
RecyclerView 动画原理 | pre-layout,post-layout 与 scrap 缓存的关系
55 0
|
API uml Android开发
Android | 深入理解View.post()获取宽高、Window加载View原理
深入理解View.post()获取宽高、Window加载View原理
319 0
|
XML 开发工具 Android开发
Android 深入了解 Window 、Activity、 View 三者关系(上)
Window、Activity、View都经常用到,但三者关系还是没有系统的理清,今天咱们就开始整理整理这三者的关系: Window:顶级窗口外观和行为策略的 抽象基类 。唯一实现是 PhoneWindow类。 Activity:四大组件之一,它提供一个界面让用户点击和各种滑动操作。 View:代表用户界面组件的基本构建块,UI 组件。
225 0
Android 深入了解 Window 、Activity、 View 三者关系(上)
|
缓存 Android开发
Android 深入了解 Window 、Activity、 View 三者关系(下)
addView 成功有一个标志就是能够接收触屏事件,通过对 setContentView 流程的分析,可以看出添加 View 的操作实质上是 PhoneWindow 在全盘操作,背后负责人是 WMS,反之 Activity 自始至终没什么参与感。但是我们也知道当触屏事件发生之后,Touch 事件首先是被传入到 Activity,然后才被下发到布局中的 ViewGroup 或者 View(Touch事件分发 了解一下)。那么 Touch 事件是如何传递到 Activity 上的呢?
212 0
|
前端开发 vr&ar 容器
Flutter 115: 图解自定义 View 之 Canvas (四) drawParagraph
0 基础学习 Flutter,第一百一十五节:自定义 Canvas 第四节,文本绘制小结!
579 0
Flutter 115: 图解自定义 View 之 Canvas (四) drawParagraph
|
Android开发 数据格式 XML
Android 面试(八):说说 Activity、View、Window 之间的关系吧
连载内容镇楼:Android 面试(一):说说 Android 的四种启动模式Android 面试(二):如何理解 Activity 的生命周期Android 面试(三):用广播 BroadcastReceiver 更新 UI 界面真的好吗?Andro...
1928 0