首先,在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++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}
}
measureChildWithMargins在ViewGroup中实现,会调用子控件的measure
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
各个布局类和控件类都实现了自己的onMeasure函数。布局类负责调用控件的measure函数。

layout调用过程:
首先DecorView的layout调用实际上是执行View的layout调用,layout实现如下:
public final void layout(int l, int t, int r, int b) {
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}

onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
}
mPrivateFlags &= ~FORCE_LAYOUT;
}
各个控件类和布局类会实现自己的onLayout,布局类的onLayout中,负责调用各个子控件的layout
draw过程
首先调用ViewRoot中的draw(bool)函数,在这里会计算需要绘制的canvas,然后将canvas作为参数传入DecorView的draw中,通过跟踪发现,首先调用View中的draw,主要进行如下步骤:
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
第四步中,调用dispatchDraw(canvas)实现绘制子控件,dispatchDraw在ViewGroup中实现,其中会对子控件进行遍历,然后调用drawChild(ViewGroup中实现)绘制子控件,drawChild根据SKIP_DRAW标志为选择调用child.draw(canvas)绘制子控件或者child.dispatchDraw(canvas)进行子空间的绘制分发。


更多相关文章

  1. android如何获取默认的桌面程序
  2. (转载)Android一些不常见的API及一些其他注意事项
  3. 2019年最新Android常用开源库汇总上篇(持续更新)
  4. Android(安卓)多线程
  5. android 数据存储技术
  6. Android基础练习
  7. 获取手机屏幕大小(DisplayMetrics类取得画面宽高)

随机推荐

  1. Spark Core读取ES的分区问题分析
  2. 重要|Spark driver端得到executor返回值
  3. 2021-03-15:手写代码:单链表选择排序。
  4. 3-15(二叉树的算法题)
  5. MongoDB系列6:MongoDB索引的介绍
  6. WEB压力测试工具Siege
  7. Python工程师需要掌握的面试题
  8. 一个有参装饰器,它可作用于任何函数上
  9. RAID原理分析总结-运维工作记录-51CTO博
  10. Python-pip安装包下载慢,怎么办?