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

1.关于MeasureSpec

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

public final void measure(int widthMeasureSpec, int heightMeasureSpec)

2.具体定义

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

3.如何产生?
顶级 view(DecorView): 窗口尺寸+自身LayoutParams 共同决定
普通View:父容器+自身LayoutParams共同决定

2.Measure过程

1.View的measure

  • view.measure为final方法,内部调用onMeasure
  • onMeasure 内部调用setMeasureDemension(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

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的宽高?

  1. view中获取宽高
    measure过程完成后可以通过getMeasureWidth/Height获取view的宽高,有些极端条件下需要在layout完成后才可以最终确定,所以简直在onLayout方法中获取最终宽高。
  2. activity中获取view的宽高
    view的measure过程与activity不是同步的,所以方法建议以下3种
    onWindowFocusChanged
    view.post(runnable)
    ViewTreeObserver
发布了37 篇原创文章 · 获赞 0 · 访问量 555

猜你喜欢

转载自blog.csdn.net/qq_37514242/article/details/104441658