View的绘制流程

1 绘制流程

View的绘制流程从ViewRootImpl的requestLayout()开始


image.png

image.png

2 measure流程

image.png
  • ViewGroup:每个ViewGroup必须复写onMeasure,并且在onMeasure中measureChild,并在measureChild结束之后,调用setMeasuredDimension设置自身的宽高。
final void measure(int widthMeasureSpec, int heightMeasureSpec){
    onMeasure(widthMeasureSpec,heightMeasureSpec);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    //measureChildren
   省略测量子布局的代码
    setMeasuredDimension(参数);
}
  • View:每个View也必须复写onMeasure,并且在onMeasure中设置自身宽高。
final void measure(int widthMeasureSpec, int heightMeasureSpec){
    //省略其它代码
    onMeasure(widthMeasureSpec,heightMeasureSpec);
}

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

影响View宽高的因素

image.png

即ViewGroup先根据每个child的MarginLayoutParams(继承自ViewGroup.LayoutParams,包括android:layout_width、android:layout_height以及各方向的margin),结合自身的MeasureSpec(32位的int类型,包含测量模式与测量大小,其中测量模式分为无约束UNSPECIFIED、精确值EXACTLY以及最大值AT _MOST三种)和padding,得到需要传给各个child的约束条件MeasureSpec,然后child根据MeasureSpec,minWidth属性以及background的最小宽高了,确定出child自身的宽高。

当某个View的measure()方法返回时,它以及它的所有子节点的getMeasuredWidth()和getMeasuredHeight()方法的值就已经设置好了。

3 layout流程

image.png
    public void layout(int l, int t, int r, int b){
        //省略其它代码
        setFrame(l, t, r, b);
        onLayout(changed, l, t, r, b);
    }

 protected boolean setFrame(int left, int top, int right, int bottom)

layout方法确定view自身的位置,通过setFrame()设置四个顶点的坐标。然后调用onLayout()确定所有子元素的位置。
所以在自定义view中,对于ViewGroup,需要重写onLayout()方法,并调用child.layout()对子元素进行布局。如果是View,由于没有子View,无需重写该方法。
注意:view可以重写layout和onLayout,而ViewGroup的只能重写onLayout,其layout是final类型。

4 draw流程

image.png··········

可以看一下,draw事件是如何从上往下传递,逐个绘制的:

image.png

5 多次measure、layout和draw

measure、layout和draw的流程有可能反反复复多次,见下图。

image.png
  • ViewGroup导致的重绘

一个父View可能对其子View调用多次measure()方法。举个例子:父节点可能首先会通过一次没有明确尺寸约束(unspecified dimensions)的测量过程来获取每个子View想获得的视图大小。如果最后得到的数值过大或者过小,那么父节点会再次对其子View调用measure()方法,并使用实际的计算结果作为输入参数(即如果子View不同意首次测量结果,父View会进行第二次带约束条件的测量)。

我们知道,顶层View为继承自FrameLayout的DecorView,看看FrameLayout的measure方法:

// FrameLayout的onMeasure函数,DecorView的onMeasure会调用这个函数。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    .....
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            ......
        }
    }
    ........
    count = mMatchParentChildren.size();
    if (count > 1) {
        for (int i = 0; i < count; i++) {
            ........
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    }
}

当然,不可能每次都执行两次measure,View的Measure函数中有相关机制,只有在FLAG_FORCE_LAYOUT标志位为1或者widthMeasureSpec、heightMeasureSpec和上次的不一样时才会重新measure。

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  ......
  // 当FLAG_FORCE_LAYOUT位为1时,就是当前视图请求一次布局操作
  //或者当前widthSpec和heightSpec不等于上次调用时传入的参数的时候
  //才进行从新绘制。
    if (forceLayout || !matchingSize &&
            (widthMeasureSpec != mOldWidthMeasureSpec ||
                    heightMeasureSpec != mOldHeightMeasureSpec)) {
            ......
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            ......
    }
    ......
}
  • ViewRootImpl导致的重绘
    Activity加载的时候,newSurface是true。
private void performTraversals() {
    ......
    boolean newSurface = false;
    //TODO:决定是否让newSurface为true,导致后边是否让performDraw无法被调用,而是重新scheduleTraversals
    if (!hadSurface) {
        if (mSurface.isValid()) {
            // If we are creating a new surface, then we need to
            // completely redraw it.  Also, when we get to the
            // point of drawing it we will hold off and schedule
            // a new traversal instead.  This is so we can tell the
            // window manager about all of the windows being displayed
            // before actually drawing them, so it can display then
            // all at once.
            newSurface = true;
                    .....
        }
    }
            ......
    if (!cancelDraw && !newSurface) {
        if (!skipDraw || mReportNextDraw) {
            ......
            performDraw();
        }
    } else {  //newSurface为true,会重新scheduleTraversals
        if (viewVisibility == View.VISIBLE) {
            // Try again
            scheduleTraversals();
        } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
                mPendingTransitions.get(i).endChangingAnimations();
            }
            mPendingTransitions.clear();
        }
    }
    ......
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容