4.关于View的绘制 (上):measure

1.关于MeasureSpec

1.1 引入的目的

用于View的测量,view.measure() 方法的两个参数分别为宽/高MeasureSpec

public final void measure(int widthMeasureSpec, int heightMeasureSpec)

1.2 具体定义

  • MeasureSpec为View的静态内部类
  • 由SpecMode(前 - 高2位) + SpecSize(后 - 低30位)两个部分组成,采用大端存储模式 (通常x86系统为小端)
  • 内部提供了打包、解包方法

1.3 如何产生

顶级 view(DecorView): 窗口尺寸+自身LayoutParams 共同决定
普通View:父容器+自身LayoutParams共同决定

2.Measure过程

2.1 View的measure

  • view.measure为final方法,内部调用onMeasure
  • onMeasure 内部调用setMeasuredDimension(int, int)
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
  • getDefaultSize 实为返回 MeasureSpec.getsize(), 即specSize
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;
    }

结束了。

总结:自定义view继承View时,什么时候需要重写onMeasure方法?为什么?
当自定义view使用wrap_content属性时,其实际大小为parentSize, 效果与match_parent一致,此时需要重写onMeasure, 可以参考ImageView / TextView

如下是简单实现,过滤出AT_MOST测量模式,主动调用setMeasureDimension方法

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        if (MeasureSpec.AT_MOST == widthSpecMode || MeasureSpec.AT_MOST == heightSpecMode) {
            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
            int size = widthSpecSize > heightSpecSize ? heightSpecSize : widthSpecSize;
            setMeasuredDimension(size, size);
        }
    }

2.2 ViewGroup的measure

ViewGroup为抽象类

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
  ...
}
  • 未提供onMeasure的统一实现,需要各个ViewGroup自己实现
  • 提供了measureChildren方法,遍历子元素并对其measure
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);
            }
        }
    }
  • measureChild就是对每个子view的测量
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);

        // 传到子view, MeasureSpec用于测量,即为上面View的测量过程
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

结束了

3.如何获取view的宽高?

3.1 view中获取宽高

measure过程完成后可以通过getMeasuredWidth/getMeasuredHeight获取view的宽高,有些极端条件下需要在layout完成后才可以最终确定,所以简直在onLayout方法中获取最终宽高。

3.2 activity中获取view的宽高

view的measure过程与activity不是同步的,所以方法建议以下3种
onWindowFocusChanged
view.post(runnable)
ViewTreeObserver

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值