View的绘制流程

认识一个Activity界面结构

每个Activity都会有一个Window,Window是抽象类具体由PhoneWindow实现。PhoneWindow中有一个顶级View即DecorView,DecorView是一个FrameLayout,有唯一的子view垂直布局的LinearLayout,包含两个子元素,一个是TitleView(ActionBar的容器),另一个是ContentView(窗口内容的容器)。关于ContentView,它是一个FrameLayout(android.R.id.content),我们平常用的setContentView就是设置它的子View。

View的绘制流程_第1张图片
Activity界面结构.png

RootView

View的测量(measure),布局(layout),绘制(draw)的三大流程都由RootView完成,RootViewImpl是RootView的具体实现类。在Activity被创建的完成后,会将DecorView添加到Window中,同时创建RootViewImpl对象,并将RootViewImpl与DecorView建立关联。View真正的绘制流程是从performTraversals开始的。

View的绘制流程

View的绘制流程_第2张图片
View的绘制流程.png

Measure测量过程

在measure测量阶段:起点是ViewRootImpl的measureHierarchy()方法---->在measureHierarchy中会执行到performMeasure(childWidthMeasureSpec, childHeightMeasureSpec),传入performMeasure()方法的MeasureSpec的SpecMode为EXACTLY,SpecSize为窗口尺寸---->在performMeasure方法中调用mView.measure(childWidthMeasureSpec, childHeightMeasureSpec),mView就是DecorView---->对于decorView来说,实际执行测量工作的是FrameLayout的onMeasure()方法。在onMeasure中调用measureChildWithMargins()方法对所有子View进行了一遍测量,并计算出所有子View的最大宽度和最大高度。

对于ViewGroup来说,它会调用child.measure()来完成子View的测量。传入ViewGroup的MeasureSpec是它的父View用于约束其测量的,那么ViewGroup本身也需要生成一个childMeasureSpec来限制它的子View的测量工作。这个childMeasureSpec就由getChildMeasureSpec()方法生成,childMeasureSpec的形成规律如下:


View的绘制流程_第3张图片
childMeasureSpec的形成.png

View的measure过程由measure方法来完成,measure方法是一个final类型的方法,子类不能重写此方法。在View的measure方法中会去调用view的onMeasure方法。

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

setMeasuredDimension会设置view测量的宽高。注意这里是测量的宽高,并非最终的宽高,虽然绝大数情况下测量宽高和最终宽高一样,但是最终的宽高还是由layout来决定。getMeasuredWidth(),getMeasuredHeight()方法用来获得测量的宽高,通过这两个方法在onMeasure中不一定能获得准确的测量宽高,因为有时候或多次调用onMeasure,所以在onLayout中去获得测量宽高是最保险的方式。

Layout布局过程

layout确定View本身的位置,onLayout确定子元素的位置

下面是view中layout的源码:先是通过setFrame的方法来确定view自己的位置,然后再onLayout的方法来确定子view的布局,onLayout没有统一的实现,不同的viewGroup由不同的实现。

    public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;

        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        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 listenersCopy =
                        (ArrayList)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;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }
getMeasureHeight()和getHeight(),测量宽高和最终宽高的区别

getMeasureHeight()是在measure之后获得,getHeight()是在layout之后获得的,两个绝大多数情况下是一样的,只有重写layout方法之后会不同。在下面这种情况下,getHeight比getMesaureHeight大100

    public void layout(int l, int t, int r, int b) {
         super.layout(l, t, r+100, b+100);
    }

draw绘制流程

实际上,View类的onDraw()方法为空,因为每个View绘制自身的方式都不尽相同,对于decorView来说,由于它是容器View,所以它本身并没有什么要绘制的。dispatchDraw()方法用于绘制子View,显然普通View(非ViewGroup)并不能包含子View,所以View类中这个方法的实现为空。ViewGroup类的dispatchDraw()方法中会依次调用drawChild()方法来绘制子View。
draw过程简单遵循如下几步:
(1)绘制背景backgroud.draw(canvas)
(2)绘制自己(onDraw)
(3) 绘制children(dispatchDraw)
(4)绘制装饰 onDrawScrollBars

你可能感兴趣的:(View的绘制流程)