zoukankan      html  css  js  c++  java
  • Android GUI之View绘制流程

      在上篇文章中,我们通过跟踪源码,我们了解了Activity、Window、DecorView以及View之间的关系(查看文章:http://www.cnblogs.com/jerehedu/p/4607599.html#gui)。那么整个Activity的界面到底是如何绘制出来的呢?既然DecorView作为Activity的顶层界面视图,那么整个界面的绘制工作应该从它开始,下面我们继续跟踪源码,看看是不是这样的。

      Activity在启动过程中会调用主线程ActivityThread中的方法performLaunchActivity和handleResumeActivity。在方法handleResumeActivity中会将创建的DecorView和WindowManagerImpl对象关联起来,关键源码部分如下:

    public final class ActivityThread {
       ……
        final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume) {
          ……
                if (r.window == null && !a.mFinished && willBeVisible) {
                    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);
                    }
    
                } 
    ……
        }
    }

      WindowManagerImpl关键代码:

        public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ……
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
          mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    ……
    }

      WindowManagerGlobal关键代码:

    public final class WindowManagerGlobal {
        ……
        public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
               ……
            ViewRootImpl root;
            View panelParentView = null;
    ……
            root.setView(view, wparams, panelParentView);
               ……
        }
    }

      根据源码调用关系,可得下图:

      从图中,我们可以看出在ActivityThread中生成的DecorView经过WindowManagerImpl、WindowManagerGlobal,最终调用了ViewRootImpl中的setView方法,将DecorView设置赋值给了ViewRootImpl中的mView属性。通过追踪ViewRootImpl我们发现最终调用了performTraversals方法,该方法关键代码如下:

    private void performTraversals() {
            // cache mView since it is used so much below...
            final View host = mView;
            ……
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            ……
            performLayout(lp, desiredWindowWidth, desiredWindowHeight);
            ……
            performDraw();
            ……
    }

      从上述源码中我们可以看出,performTraversals实际上依次调用了三个关键的方法,分别是performMeasure,performLayout、performDraw。

      1、方法performMeasure,内部实际上调用了mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);不要忘了此处的mView正是我们传递进来的DecorView,该方法用于测量View的大小。关键源码如下:

            private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
            try {
                mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }

      2、方法performLayout,内部实际上调用了host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());该方法用于确定视图的位置。关键源码如下:

        private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                int desiredWindowHeight) {
            mLayoutRequested = false;
            mScrollMayChange = true;
            mInLayout = true;
    
            final View host = mView;
            ……
            try {
                host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    
                mInLayout = false;
                int numViewsRequestingLayout = mLayoutRequesters.size();
                if (numViewsRequestingLayout > 0) {
                  ……
                    if (validLayoutRequesters != null) {
                        // Set this flag to indicate that any further requests are happening during
                       ……
                        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    
                       ……                   
     }
                    }
    
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            mInLayout = false;
        }

      3、方法performDraw,用于绘制视图,追踪源码发现,最终调用了mView.draw(canvas)方法,用于绘制。

      经过上述过程基本上可以确定View的绘制流程,流程图具体如下:

      疑问咨询或技术交流,请加入官方QQ群:JRedu技术交流 (452379712)

    作者:杰瑞教育
    出处:http://www.cnblogs.com/jerehedu/ 
    本文版权归烟台杰瑞教育科技有限公司和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
     
  • 相关阅读:
    angularjs的$on、$emit、$broadcast
    angularjs中的路由介绍详解 ui-route(转)
    ionic入门教程-ionic路由详解(state、route、resolve)(转)
    Cocos Creator 加载使用protobuf第三方库,因为加载顺序报错
    Cocos Creator 计时器错误 cc.Scheduler: Illegal target which doesn't have uuid or instanceId.
    Cocos Creator 构造函数传参警告 Can not instantiate CCClass 'Test' with arguments.
    Cocos Creator 对象池NodePool
    Cocos Creator 坐标系 (convertToWorldSpaceAR、convertToNodeSpaceAR)
    Cocos Creator 常驻节点addPersistRootNode
    Cocos Creator 配合Tiled地图的使用
  • 原文地址:https://www.cnblogs.com/jerehedu/p/4679534.html
Copyright © 2011-2022 走看看