-
DecorView
窗口的頂級布局:包含了titlebar(標(biāo)題欄)、content(內(nèi)容android.R.id.content)
通過android.R.id.content獲得內(nèi)容布局的Parent層:
ViewGroup content = (ViewGroup)findViewById(android.R.id.content) ;
View view = content.getChildAt(0);
View的measure過程
- MeasureSpec 代表32位的int值,高2位代表SpecMode(測量模式) ,低30位代表SpecSize(測量模式下的規(guī)格大小)
1.UNSPECIFIED
父容器不對Viewyou任何限制,要多大給多大,這種情況一般用于系統(tǒng)內(nèi)部,表示一種測量的狀態(tài),一般屬于系統(tǒng)內(nèi)部多次Measure的情形,一般來說不需要關(guān)注此模式
2.EXACTLY
父容器已經(jīng)檢測出View所需要的精確大小,這個(gè)時(shí)候View的最終大小就是SpecSize所指定的的值,他對應(yīng)于LayoutParams中的match_parent 和具體的數(shù)值這兩種模式
3.AT_MOST
父容器指定了一個(gè)可用大小即SpecSize,View 的大小不能大于這個(gè)值具體是什么要看不同View的具體實(shí)現(xiàn)。他對應(yīng)于LayoutParams中的wrap_content
| parentSpecMode | EXACTLY | AT_MOST | UNSPECIFIED |
|---|---|---|---|
| chlidLayoutParams | |||
| dp/px(固定數(shù)值) | 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 是一樣的結(jié)果都是parent剩余的大小,這樣跟我們的實(shí)際結(jié)果是不一樣的,實(shí)際上在View這種情況先是進(jìn)行了一定的處理的
具體代碼是這樣的:
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之后獲得,好的習(xí)慣是在onLayout中獲得,但是有的時(shí)候我們在Activity的onCreat中獲得measure寬高,但是View沒有測完,所以為0,現(xiàn)在解決方案有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)
這種比較復(fù)雜,根據(jù)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的實(shí)現(xiàn)可以知道,View的尺寸使用30位二進(jìn)制表示也就是說最大是30個(gè)1(即2^30 -1 ),也就是(1<<30)-1,在最大化模式下,我們用View理論上能支持的最大值去構(gòu)造MeasureSpec是合理的
+ 具體數(shù)值(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);
錯(cuò)誤用法:無法通過錯(cuò)誤的MeasureSpec去得出合法的SpecMode,從而導(dǎo)致measure過程出錯(cuò)
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);