zoukankan      html  css  js  c++  java
  • 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)进行子空间的绘制分发。
  • 相关阅读:
    第一个C#程序
    定位网页元素
    盒子模型
    浮动
    css3美化网页元素
    html css3
    java表单基础
    表单
    html5基础
    java程序题目解析
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744652.html
Copyright © 2011-2022 走看看