一、View的加载流程:
1、Activity调用setContentView方法,这里的getWindow是PhoneWindow,接着调用它的setContentView。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
ContentParent为空则调用installDecor方法,创建一个DecorView,找到一个Id为com.android.internal.R.id.content的ViewGroup,设置给mContentParent。
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
.....
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
.....
}
2、在ActivityThread类的方法handleResumeActivity里面调用addView,将decorView添加到WindowManager中。
//wm就是WindowManagerImpl
wm.addView(decor, l);
//mGlobal就是WindowManagerGlobal
mGlobal.addView(view, params, mDisplay, mParentWindow);
root = new ViewRootImpl(view.getContext(), display);
在WindowManagerGlobal类中的addView方法里面调用rootViewImpl类的setView方法,开始绘制View树。
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
二、View绘制流程
1、UI绘制流程经历了五个阶段,分别是预测量阶段,布局窗口阶段,最终测量阶段,布局控件树阶段,绘制阶段。
A、预测量阶段,会对控件树进行第一次测量,将会计算出控件树为显示其内容所需的尺寸,即期望的窗口尺寸,View及其子类的onMeasure方法将会沿着控件树依次得到回调。
B、布局窗口阶段,根据预测量的结果,通过IWindowSession.relayout方法向WMS请求调整窗口的尺寸等属性,将引发WMS对窗口进行重新布局,并将布局结果返回给ViewRootImpl。
C、最终测量阶段,将以窗口的实际尺寸对控件进行最终测量,View及其子类的onMeasure方法将会沿着控件树依次被回调。
D、布局控件树阶段,确定控件的位置,View及其子类的onLayout方法将会被回调。
E、绘制阶段,对控件树进行绘制,View及其子类的onDraw方法将会被回调。
三、实例演示
通过WindowManger类的addView将自定义悬浮窗口添加到DecorView上
private void installFloatingWindow() {
wm= (WindowManager)getSystemService(Context.WINDOW_SERVICE);
lp=newWindowManager.LayoutParams();
lp.type= WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
lp.format= PixelFormat.RGBA_8888;
lp.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
lp.setTitle("Floating Window!");
lp.gravity= Gravity.LEFT| Gravity.TOP;
lp.x=0;
lp.y=0;
lp.width= Utils.getScreenWidth(this) /3;
lp.height= Utils.getScreenHeight(this) /3;
//用于检测状态栏高度.
intresourceId = getResources().getIdentifier("status_bar_height","dimen","android");
if(resourceId >0)
{
statusBarHeight= getResources().getDimensionPixelSize(resourceId);
}
LayoutInflater inflater = LayoutInflater.from(getApplication());
//获取浮动窗口视图所在布局.
floatLayout= (CustomToolBar) inflater.inflate(R.layout.floatlayout,null);
floatLayout.setToolBarClickListener(this);
floatLayout.setOnTouchListener(this);
wm.addView(floatLayout,lp);
}