View的工作流程-measure、layout、draw三大流程

measure过程

  • View:直接完成其测量的过程
  • ViewGroup:测量自己和它的子元素

    View的measure过程:

    • 最开始是View的measure方法
    • measure方法里面调用了onMeasure方法,onMeasure方法的源码为:
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

它是设置View的宽高测量值。其中的getDefaultSize方法返回的是measureSpec中的SpecSize。可参考MeasureSpec的理解了解一下MeasureSpec。
getSuggestedMinimumWidth与getSuggestedMinimumHeight返回的规则是:如果View有设置背景,则返回该背景对应Drawable的原始宽高;否则返回指定的android:minWidth和android:minHeight.
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;
    }

从中和集合MeasureSpec的SpecMode可以注意到,如果直接继承View来自定义控件,如果没有重写onMeasure方法并设置wrap_content的自身大小,则wrap_content会默认是match_parent。
解决(套路):

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode=MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
        int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode==MeasureSpec.AT_MOST && heightSpecMode==MeasureSpec.AT_MOST){
              setMeasuredDimension(默认的width,默认height);
        }else if (widthSpecMode==MeasureSpec.AT_MOST){
             setMeasuredDimension(默认的width,heightSpecSize);
        }else if (heightSpecMode==MeasureSpec.AT_MOST){
             setMeasuredDimension(widthSpecSize,默认的height);
        }
    }

ViewGroup的measure过程:

  • measureChildren方法,在此中遍历子View,
  • 调用measureChild方法对每个子View进行测量
  • measureChild:获取子View的MeasureSpec,然后调用每个子View的measure方法

注意:很多时候我们不知道View到底在何时测量完毕,所有在获取宽高时要采用特定的办法。

  • 采用onWindowFocusChanged:View已经测量完毕,当Activity的窗口得到焦点或者失去焦点时均会被调用。
  • view.post(runnable) : 通过消息机制去获取。
  • ViewTreeObserver:使用它的OnGlobalLayoutListener接口,可监听View树的变化,当View的可见性发生变化时,onGlobalLayout会被调用,这时的View也是测量好的了。
  • 自己手动调用:view.measure(int widthMeasureSpec, int heightMeasureSpec)。

Layout过程

Layout过程是用来确定View的具体位置的。

1.View的layout过程

  • 在layout方法中通过setFrame方法设定View的四个定点的位置
  • 调用onLayout方法:确定本身的位置,与onMeasure相似,都是与具体的布局有关,在View中没有具体的实现。

    2.ViewGroup的layout过程

    没有具体实现onLayout方法。具体的View有着不同的实现

draw过程

public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // we're done...
            return;
        }
   ......

根据注释的步骤:

  • drawBackground(canvas):绘制背景
  • onDraw(canvas):绘制自己
  • dispatchDraw(canvas):绘制children
  • onDrawForeground(canvas):绘制装饰

特殊方法:setWillNotDraw(boolean willNotDraw)
该方法用来设置是否绘制内容。
true:不绘制,然后系统还会进行优化。
false:绘制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值