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++) {
            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)进行子空间的绘制分发。

你可能感兴趣的:(Android控件绘制过程)