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的宽高?
- view中获取宽高
measure过程完成后可以通过getMeasureWidth/Height获取view的宽高,有些极端条件下需要在layout完成后才可以最终确定,所以简直在onLayout方法中获取最终宽高。 - activity中获取view的宽高
view的measure过程与activity不是同步的,所以方法建议以下3种
onWindowFocusChanged
view.post(runnable)
ViewTreeObserver