-
DecorView
窗口的顶级布局:包含了titlebar(标题栏)、content(内容android.R.id.content)
通过android.R.id.content获得内容布局的Parent层:
ViewGroup content = (ViewGroup)findViewById(android.R.id.content) ;
View view = content.getChildAt(0);
View的measure过程
- MeasureSpec 代表32位的int值,高2位代表SpecMode(测量模式) ,低30位代表SpecSize(测量模式下的规格大小)
1.UNSPECIFIED
父容器不对Viewyou任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态,一般属于系统内部多次Measure的情形,一般来说不需要关注此模式
2.EXACTLY
父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的的值,他对应于LayoutParams中的match_parent 和具体的数值这两种模式
3.AT_MOST
父容器指定了一个可用大小即SpecSize,View 的大小不能大于这个值具体是什么要看不同View的具体实现。他对应于LayoutParams中的wrap_content
parentSpecMode | EXACTLY | AT_MOST | UNSPECIFIED |
---|---|---|---|
chlidLayoutParams | |||
dp/px(固定数值) | EXACTLY childSize | EXACTLY childSize | EXACTLY childSize |
match_parent | EXACTLY parentSize | AT_MOST parentSize | UNSPECIFIED 0 |
wrap_content | AT_MOST parentSize | AT_MOST parentSize | UNSPECIFIED 0 |
从上边的表中可以看出View的match_parent 和 wrap_content 是一样的结果都是parent剩余的大小,这样跟我们的实际结果是不一样的,实际上在View这种情况先是进行了一定的处理的
具体代码是这样的:
public class mView extends View {
public mView(Context context) {
super(context);
}
private int mWidth = 0;
private int mHeight = 0;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, mHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(mWidth, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, mHeight);
}
}
}
- 在measure之后通过getMeasureHeight/Height ,但是尽量不要在measure之后获得,好的习惯是在onLayout中获得,但是有的时候我们在Activity的onCreat中获得measure宽高,但是View没有测完,所以为0,现在解决方案有4种:
- Activity /View#onWindowFocusChanged
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
}
}
- view.post(runnable)
@Override
protected void onStart() {
super.onStart();
view.post(new Runnable() {
@Override
public void run() {
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
Log.e(TAG, "onStart()");
}
- ViewTreeObserver
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
}
});
- view.measure(int widthMeasureSpec, int heightMeasureSpec)
这种比较复杂,根据View的LayoutParams来分:- match_parent
还是直接放弃吧 - wrap_content
- match_parent
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
view.measure(widthMeasureSpec, heightMeasureSpec);
注意到(1<<30)-1 ,通过分析MeasureSpec的实现可以知道,View的尺寸使用30位二进制表示也就是说最大是30个1(即2^30 -1 ),也就是(1<<30)-1,在最大化模式下,我们用View理论上能支持的最大值去构造MeasureSpec是合理的
+ 具体数值(dp/px)
//比如宽/高 都是100px 如下measure
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
view.measure(widthMeasureSpec,heightMeasureSpec);
错误用法:无法通过错误的MeasureSpec去得出合法的SpecMode,从而导致measure过程出错
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( -1, View.MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( -1, View.MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec, heightMeasureSpec);
view.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);