ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl和Decor-View建立关联。
DecorView作为顶级View,一般情况下,它的内部会包含一个竖直的LinearLayout,LinearLayout有上下两部分(具体看Android版本和当前的主题),上面是标题栏下面是内容栏。在Activity我们通过setContentView所设置的布局就是被添加到内容栏中的,而内容栏的id是content,布局是FrameLayout。
得到content:
findViewById(R.android.id.content);
得到我们设置的view:
findViewById(R.android.id.content).getChildAt(0);
DecorView是一个FrameLayout。
MeasureSpec代表一个32位的int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode代表测量模式,而SpecSize是指在某种测量模式下的规格大小。
MeasureSpec的简单说明
http://blog.csdn.net/chunqiuwei/article/details/44515345
Java 位运算(移位、位与、或、异或、非)
http://blog.csdn.net/xiaochunyong/article/details/7748713
MeasureSpec的代码很少:
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec( int size, int mode) {
if ( sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return ( size & ~ MODE_MASK) | ( mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) {
return ( measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return ( measureSpec & ~ MODE_MASK);
}
static int adjust(int measureSpec, int delta) {
return makeMeasureSpec(getSize (measureSpec + delta ), getMode(measureSpec));
}
}
把SpecMode和SpecSize打包成int值,提供解包方法,方便操作。
SpecMode:
UNSPECIFIED
父容器不对view有任何限制,要多大,给多大,这种情况一般用于系统部,表示一种测量状态。
EXACTLY
父容器已经检测出view所需要的精确大小,这个时候,view的最终大小就是SpecSize所指定的值,它对应于LayoutParams中的match_parent和具体的数值这两种模式。
AT_MOST
父容器指定了一个可用大小即SpecSize,view的大小不能大于这个值,具体是什么值,要看不同view的具体实现,它对应于LayoutParams中的warp_content。
表 普通view的MeasureSpec的创建规则:
解释:当view采用固定宽高的时候,不管父容器的MeasureSpec是什么,view的MeasureSpec都是精确模式并且其大小遵循Layoutparams中的大小。
当view的宽高是match_parent的时候,如果父容器的模式是精确模式,那么view也是精确模式并且其大小不会超过父容器的剩余空间,如果父容器是最大模式,那么view也是最大模式并且大小不会超过父容器的剩余空间。
当view的宽高是warp_content时,不管父容器的模式是精确还是最大化,view的模式总是最大化并且大小不能超过父容器的剩余空间。
View的工作流程主要是指measure,layout,draw这三大流程,分别测量宽高,确定位置,画出来。
Measure:
在Activity里面获取view的宽高:
(1)Activity/view的onWindowFocusChanged
这个方法的含义是:view已经初始化完毕了,宽高已经准备好了。需要注意的是这个方法会被调用多次,当Activity的窗口得到焦点和失去焦点时均会被调用,具体如果频繁的onResume和onPause
,那么就会多次调用。
public void onWindowFocusChanged( boolean hasFocus) {
super.onWindowFocusChanged( hasFocus);
if( hasFocus) {
int width = tv.getMeasuredWidth();
int height = tv.getMeasuredHeight();
}
}
(2)view.post(Runnable)
通过post可以将一个runnable投递到消息队列的尾部,然后等待Looper调用此Runnable的时候,view也已经初始化好。
protected void onStart() {
super.onStart();
tv.post( new Runnable() {
@Override
public void run() {
int width = tv.getMeasuredWidth();
int height = tv.getMeasuredHeight();
}
});
}
(3)ViewTreeObserver
使用ViewTreeObserver的众多回调可以完成这个功能,比如使用onGlobalLayoutListener这个接口,当view树的状态发生变化或者view树内部的view的可见性发生改变时,onGlobalLayout方法将被回调,因此这是获取view的宽高的一个很好的时机。
注意:随着view树状态的发生的变化,会被调用多次。
protected void onStart() {
super.onStart();
ViewTreeObserver observer = mr.getViewTreeObserver();
observer.addOnGlobalLayoutListener( new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mr.getViewTreeObserver(). removeGlobalOnLayoutListener( this);
int width = mr.getMeasuredWidth();
int height = mr.getMeasuredHeight();
}
});
}
(4)view.measure(int widthMeasureSpec,int heightMeasureSpec )
需要根据view的LayoutParams来分:
mach_parent:无法Measure出来。
具体数值:
int widthMeasureSpec = MeasureSpec. makeMeasureSpec(100, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec. makeMeasureSpec(100, MeasureSpec.EXACTLY);
mr.measure( widthMeasureSpec, heightMeasureSpec);
warp_content:
如下Measure:
int widthMeasureSpec = MeasureSpec. makeMeasureSpec((1<<30)-1, MeasureSpec. AT_MOST);
int heightMeasureSpec = MeasureSpec. makeMeasureSpec((1<<30)-1, MeasureSpec. AT_MOST);
mr.measure( widthMeasureSpec, heightMeasureSpec);
用view在理论上能支持的最大的值去构造MeasureSpec是合理的。
两种错误的用法:
int widthMeasureSpec = MeasureSpec. makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED );
int heightMeasureSpec = MeasureSpec. makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED );
mr.measure( widthMeasureSpec, heightMeasureSpec);
mr.measure( LayoutParams.WRAP_CONTENT, LayoutParams. WRAP_CONTENT);
Layout:
View的getMeasureWidth和getWidth这两个方法有什么区别?
View的默认实现中,是相等的,只是赋值的时机不同,实际开发中可能会不相等。
public void layout(int l,int t,int r,int b) {
super.layout(l,t,r+100,b+100);
}
Draw:
view的绘制过程遵循下面几个步骤:
(1)绘制背景background.draw(canvas)
(2)绘制自己(onDraw)
(3)绘制children(dispatchDraw)
(4)绘制装饰(onDrawScrollBars)
4.自定义View
分类:
1.继承view,重写onDraw方法。
2.继承ViewGroup派生特殊的Layout。
3.继承特定的View如TextView。
4.继承特定的ViewGroup如LinearLayout。
须知:
1.让view支持warp_content
在onMeasure里面处理。
2.如有必要,让view支持padding
View要在onDraw方法中要处理padding,而ViewGroup要在onMeasure和onLayout中处理padding和margin。
3.尽量不要在view中使用HandlerView内部提供了post系列方法供使用。
4.View中如果有线程或者动画,需要及时停止在View的onDetachedFromWindow中停止动画,线程或回收其他资源。
5.View带有滑动嵌套的时候,需要处理好滑动冲突需要处理好滑动冲突。