源码带你分析View的工作原理

初识ViewRoot和DecorView
前言

        viewRoot对应于ViewRootImpl类,是连接WindowManager和DecorView的枢纽带,View的三个流程均是通过ViewRoot完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并将ViewRootImpl和DecorView建立联系,源码如下:

root=new ViewRootImpl(view.getContext,display);
root.setView(view,wparams,panelParentView);

        View的绘制由ViewRoot的performTraversals方法开始的。经过measure、layout和draw三个故此才能最终把View绘制出来。

performTraversals
  • performMeasure-Measure-Measure
  • performLayout-Layout-onLayout
  • performDraw-Draw-onDraw
DecorView

DecorView作为顶级的View,一般LinearLayout,包含两个部分,如图:
这里写图片描述
MeasureSpec:View的尺寸规格

由SpecMode(测量模式)+ SpecSize(规格大小)组成,主要SpecMode有三类
- UNSPECIFIED
父容器对View没有任何的限制,要多大有多大
- EXACTLY
View的精确大小,相当于match_parent
- AT_MOST
View不能大于该值,相当于wrap_content


对于顶级的DecorView,MeasureSpec由自身的LayoutParams和窗口尺寸决定,对于普通的View,MeasureSpec由自身的LayoutParams和父容器的MeasureSpec决定。

View的工作流程

View的工作流程主要由measurelayoutdraw三个流程完成,即测量、布局和绘制。
- 对于View:measure:测量,决定View宽/高会调用onMeasure方法:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

我们来看getDefaultSize方法:


    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

对于AT_MOST和EXACTLY两种情况,specSize就是View测量后的大小。View的最终大小也是layout阶段决定的。 但是几乎所有情况View的测量大小和最终大小是相等的。
对于UNSPECIFIED这种情况,一般用于系统内部的测量过程,View的大小是getDefaultSize的第一个参数size.即宽和高分别为

protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }                  
protected int getSuggestedMinimumHeight() {
        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
    }
  • 如果没有给View设置背景,那么就返回View本身的最小宽度mMinWidth

  • 如果给View设置了背景,那么就取View本身最小宽度mMinWidth和背景的最小宽度的最大值

再说下从getDefaultSize方法实现得知:View的大小由specSize决定,我们可以得到以下定论:如果不重写View的onMeasure方法并且设置wrap_content,那么相当于使用match_parent.d对于wrap_content时一般对onMeasure方法作过特殊处理,比如查看TextView的onMeasure方法得知,有

     if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(widthSize, width);
            }

  if (heightMode == MeasureSpec.AT_MOST) {
                height = Math.min(desired, heightSize);
            }
  • ViewGroup的measure的过程
  • ViewGroup,对于自己measure的方法外,还会遍历子元素的measure方法,各个子元素再递归地执行这个过程。和View不同的是,ViewGroup是抽象类,本身并没有去重写View的onMeasure方法,但是提供了一个measureChildren方法,如下:
    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    由上面代码,可以看出,ViewGroup在measure方法去多每个子元素measure,measureChildren方法也很简单,如下:

    protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

    显然可以得知,measureChildren的思想是取出每个子元素的LayParams,然后通过getChildMeasureSpec方法创建子元素的MeasureSpec,直接传给View的measure方法。
    我们知道,ViewGroup没有定义测量的过程,并且因为不同的子类有不同的测量过程,故此没有像View对onMeasure方法作统一的实现.比如,LinearLayout的onMeasure方法实现如下:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

今晚先到这里,明天晚上再补上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值