Android控件绘制过程
首先,在activity 类中(activity.java),我们可以看到两个变量,分别是: private Window mWindow;
private WindowManager mWindowManager;
这两个变量在attach函数中进行赋值,
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
mWindowManager = mWindow.getWindowManager();
查看PolicyManager.makeNewWindow的具体实现可以看到,返回的是PhoneWindow对象(Policy.java中实现)。PhoneWindow是Window的派生类。跟踪setWindowManager我们可以得到WindowManager对象,并且这个对象是在系统唯一的,这个对象同样被赋值给Window的成员变量mWindowManager。一个Activity包含一个PhoneWindow,所有UI都被包含在PhoneWindow中。
在PhoneWindow类中包含两个和View相关成员变量,分别是
private DecorView mDecor;
private ViewGroup mContentParent;
我们知道,在Android平台上,UI界面是通过View和ViewGroup分层树进行定义的,如下图所示。
最顶层的是ViewGroup,而DecorView就是PhoneWindow的View框架最顶层的根,DecorView是FrameLayout的派生类。在installDecor(PhoneWindow.java)中对mContentParent进行赋值
mContentParent = generateLayout(mDecor);
在generateLayout函数中(PhoneWindow.java)有如下实现:
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
mContentParent是从layoutResource中的布局xml中获得的,所有的activity用户新增加view都会被包含在这个对象当中。
我们在新建一个activity时,往往在OnCreate中调用setContentView(R.layout.main)定义UI界面,跟踪setContentView发现实际上是调用了PhoneWindow的setContentView函数,在setContentView中,首先调用installDecor,对mDecor和mContentParent进行初始化,然后调用mLayoutInflater.inflate(layoutResID, mContentParent)从XML文中中生成相应的View并将用户新增的view添加到mContentParent对象当中。这个过程中会调用View的onFinishInflate。
ViewRoot是Handler的派生类,在整个显示系统中最为关键,在android的UI结构中扮演的是一个中间者的角色,连接PhoneWindow跟WindowManagerService.
WindowManger维护了一个三元组View, ViewRoot, LayoutParams数组,在ActivityThread类的handleResumeActivity中,有如下代码:
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
首先获得WindowManger(通过跟踪发现a.getWindowManager()返回WindowManger对象),然后调用WindowManger的addView方法(WindowManagerImpl中实现)将PhoneWindow的DecorView添加到WindowManger中,同时为它创建一个ViewRoot对象。在addView中,将进行如下调用
root.setView(view, wparams, panelParentView);
在ViewRoot的setView中将调用requestLayout,在requestLayout中会调用scheduleTraversals,而scheduleTraversals只是简单的发送处理消息DO_TRAVERSAL;我们知道ViewRoot是一个handler,所以接下来消息循环下一个将调用的就是ViewRoot的handleMessage。
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL:
if (mProfile) {
Debug.startMethodTracing("ViewRoot");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
break;
performTraversals函数相当复杂,完成了View框架从上到下的绘制,函数调用流程图如下:
dispatchAttachedToWindow调用过程:
首先,判断mFirst标志位,只有mFirst为true,即第一次调用的时候才调用ViewRoot对应的View的dispatchAttachedToWindow,这个View就是PhoneWindow中的DecorView。我们知道DecorView继承FrameLayout,是一个ViewGroup,dispatchAttachedToWindow实现如下:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
super.dispatchAttachedToWindow(info, visibility);
visibility |= mViewFlags & VISIBILITY_MASK;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
children[i].dispatchAttachedToWindow(info, visibility);
}
}
View的dispatchAttachedToWindow中会调用onAttachedToWindow函数,各个控件可以重写onAttachedToWindow实现自己的操作。
由上述代码可以知道,在View框架整个调用过程 ,如果是非叶节点,首先调用父类的dispatchAttachedToWindow,然后调用子节点的dispatchAttachedToWindow;如果是叶节点,则会调用View的dispatchAttachedToWindow,整个调用过程可以看成树的先序遍历。
measure调用过程:
DecorView的measure函数,首先调用View的measure实现,measure中会调用FrameLayout重写onMeasure,具体实现如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
// Find rightmost and bottommost child
for (int i = 0; i < count; i++) {
补充:移动开发 , Android ,