美文网首页
从源码看onMeasure、onLayout、onDraw

从源码看onMeasure、onLayout、onDraw

作者: 冬冬269 | 来源:发表于2018-09-05 19:36 被阅读0次

    1.View

    measure(int widthMeasureSpec, int heightMeasureSpec)

    做下判断forcelayout,needslayout,调用onMeasure方法。

    onMeasure(widthMeasureSpec,HeightMeasureSepc)

    MeasureSpec

    干嘛的

    int值,里面封装了size和mode。节省内存。在view的OnMeasur中主要就是解析widthMeasureSpec和hightMeasureSpec中的mode和size。根据其中的mode和size,得到view的测量后大小。

    哪来的。

    是由这个view的父空间viewgroup的MeasureSpec和view本身的LayoutParams来决定的,父空间调用getChildMeasureSpec来创建。

    image.png

    源码分析结束后,可以得到上面一个表。源码不贴了。太占地方,这个表是某大神画的。创建好了之后就调用view.measure,view.measure在调用onmeasure,这个measurespec就给到view了。

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

    看一下getDefaultSize(int,int)

     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;
        }
    

    如果measureSpec的mode是UNSPECIFIED。宽/高=size。这个size是多少呢。我们看getSuggestedMinimumWidth()

     protected int getSuggestedMinimumWidth() {
            return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
        }
    

    如果mode是EXACTLY。宽/高 = measureSpec自带的,根据表可知道,就是自身的设置的宽高。
    如果mode是AT_MOST。宽/高 = measureSpec自带的,根据表可知,就是父类剩余的宽高。

    是否设置背景图,没有就mMinWidth 系统默认,一般是0,由就背景图宽高。

    那么我们知道了之后,如果我们设置了wrapcontent,不想使用父类的剩余宽高,我们可以在onMeasure做判断,通过setMeasureDimension然后自己设置宽高。代码如下

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
    
            if(widthMode == MeasureSpec.AT_MOST &&heightMode == MeasureSpec.AT_MOST){
                setMeasuredDimension(200,200);
    
            }else if(widthMode == MeasureSpec.AT_MOST){
                setMeasuredDimension(100,height);
            }else if(heightMode == MeasureSpec.AT_MOST){
                setMeasuredDimension(width,100);
            }
    

    viewgroup的measure

    image.png

    没有自己onMeasure方法和measure方法,提供了measureChildren方法直接遍历子view,去创建他们的measureSpec,然后调用view的measure把measureSpec传过去进行测量。

    viewgroup是一个抽象类,onMeasure需要子类去实现。因为不同的viewgroup子类有不同的特性,无法统一实现。大概看了一下linearLayout的onMeasure方法,有vertical和horizontal。matchparent或者固定宽高的onmeasure和view一样,不同的是子view测量完毕后,会绘制linearlayout。根据所以子空间的高度加起来来绘制linearlayout,不多说了。

    layout

    layout用来确定自身位置,自身位置确定后,调用onlayout确定子view的位置。view和viewgroup没有实现onlayout。具体实现是由子类实现的。
    layout中调用setframe,onlayout中调用setChildframe。

    getMeasureWidth和getWidth的区别。一个形成于measure,一个在layout。正常情况是相等的。在layout中super.layout(l,t,r+100,b+100)。那么就不相等,但没什么意义。

    draw

    dispatchdraw,调用子的draw方法。
    drawBackgrodun(canvas);
    ondraw(canvas);
    dispatchdraw(canvas)
    ondrawScroolBars(canvas)

    setWillNotDraw,当一个view不需要绘制任何内容时,这个标记为true,系统会进行相应优化。viewgroup默认开启。我们的自定义viewgroup如果不需要绘制可以设为true,明确知道需要ondraw来绘制内容,我们需要显示关闭WILL_NOT_DRAW标记位。

    onDetachenFromWindow 处理view被关闭。

    相关文章

      网友评论

          本文标题:从源码看onMeasure、onLayout、onDraw

          本文链接:https://www.haomeiwen.com/subject/xavliftx.html