Android界面绘制流程

本文详细介绍了Android界面的绘制流程,从ViewRoot开始,讲解了MeasureSpec的测量模式及其在View测量过程中的作用,再到View的具体绘制步骤,包括measure、layout和draw阶段。重点阐述了DecorView的结构和其在窗口管理中的地位,以及Activity如何通过WindowManager将内容展现给用户。

1.ViewRoot

ViewRoot是连接WindowManager与DecorView的纽带,View的整个绘制流程的三大步(measure、layout、draw)都是通过ViewRoot完成的。当Activity对象被创建完毕后,会将DecorView添加到Window中(Window是对窗口的抽象,DecorView是一个窗口的顶级容器View,其本质是一个FrameLayout),同时会创建ViewRootImpl(ViewRoot的实现类)对象,并将ViewRootImpl与DecorView建立关联。关于ViewRoot,我们只需要知道它是联系GUI管理系统和GUI呈现系统的纽带。View的绘制流程从ViewRoot的performTraversals方法开始,经过measure、layout、draw三大过程完成对一个View的绘制工作。peformTraversal方法内部会调用measure、layout、draw这三个方法,这三个方法内部又分别调用onMeasure、onLayout、onDraw方法。

在onMeasure方法中View会对其所有的子元素执行measure过程,此时measure过程就从父容器”传递”到了子元素中,接着子元素会递归的对其子元素进行measure过程,如此反复完成对整个View树的遍历。onLayout与onDraw过程的执行流程与此类似。

2. MeasureSpec

MeasureSpec为一个32位的int值,高2位代表SpecMode,低30位代表SpecSize,前者指测量模式,后者指某种测量模式下的规格大小。在一个View的measure过程中,系统会将该View的LayoutParams结合父容器的“要求”生成一个MeasureSpec,这个MeasureSpec说明了应该怎样测量这个View

(1)三种 SpecMode:

UNSPECIFIED:父容器不对View作任何要求,通常用于系统内部,表示一种测量的状态。

EXACTLY:父容器已经检测出View所需要的精确大小,这种测量模式下View的测量值就是SpecSize的值。这个SpecMode对应于LayoutParams中的match_parent和给出具体大小这两种模式。

AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于此值,可用大小取决于不同View的具体实现。这个SpecMode对应于LayoutParams中的wrap_content。

(2)对于DecorView,它的MeasureSpec由窗口尺寸和其自身的LayoutParams共同确定;对于普通View,他的MeasureSpec由父容器的MeasureSpec和其自身的LayoutParams共同确定。

3. View**的具体绘制流程**

(1)measure过程

DecorView的measure过程

*    *前面我们提到过,DecorView是一个应用窗口的根容器,它本质上是一个FrameLayout。DecorView有唯一一个子View,它是一个垂直LinearLayout,这个垂直线性布局管理器包含两个子元素,一个是TitleView(ActionBar的容器),另一个是ContentView(窗口内容的容器)。关于ContentView,它是一个FrameLayout(android.R.id.content),我们平常用的setContentView就是设置它的子View。如下图中,我们为TilteView中添加了一个ActionBar,为ContentView中添加了一个RelativeLayout(通过setContentView方法)。

img

    前面提到,DecorView的MeasureSpec由窗口的尺寸和自身的LayoutParams共同决定。在ViewRootImpl的measureHierarchy方法中,完成了创建DecorView的MeasureSpec的过程,相应的代码片段如下:

1 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
2 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
3 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    以上代码片段中的childXxxMeasureSpec即为DecorView的MeasureSpec,lp.width和lp.height被系统赋值为MATCH_PARENT。

https://i-blog.csdnimg.cn/blog_migrate/3cf27ccfdf33d19058fd4604fbd14e83.png

一、DecorView为整个Window界面的最顶层View。

二、DecorView只有一个子元素为LinearLayout。代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域。

三、LinearLayout里有两个FrameLayout子元素。

  (20)为标题栏显示界面。只有一个TextView显示应用的名称。也可以自定义标题栏,载入后的自定义标题栏View将加入FrameLayout中。

  (21)为内容栏显示界面。就是setContentView()方法载入的布局界面,加入其中。

下图为SDK中tools文件夹下hierarchyviewer bat 查看ViewTree的结果:

(此时未替换标题栏)

img

2.替换标题栏后ViewTree的变化:

img

绿色区域发生了变化,改变为了载入的title.xml文件的布局。

整理流程梳理可以参考下面这张图片:

img

我们了解了上面得到流程后下面梳理一下如何进入到 view 的绘制流程:

ViewRoot 对应的实现类是 ViewRootImpl 类,他是连接 WindowManager 和DecorView 的纽带,view
的三大 流程均是通过 ViewRoot 来完成的。在 ActivityThread 中,当 activity 对象被创建完毕后,会将
DecorView 添加到Window 中,同时会创建 ViewRootImpl 对象,并将 ViewRootImpl 对象和
DecorView 建立关联。

DecorView是顶级View,内部有titlebar和contentParent两个子元素,contentParent的id是content,而我们设置的main.xml布局则是contentParent里面的一个子元素。

DecorView生成的过程:

这里写图片描述

把已经创建好并初始化好的DecorView添加并显示到屏幕的过程:

这里写图片描述

其实在Android中真正展示给用户的是window和view,activity在android中所其的作用主要是处理一些逻辑问题,比如生命周期的管理、建立窗口等。在android中,窗口的管理还是比较重要的一块,因为他直接负责把内容展示给用户,并和用户进行交互。响应用户的输入等。

在讲窗口管理时,有必要先说下ViewManager这个接口,这个接口主要有以下的实现子接口和实现类,分别是:WindowManager和ViewGroup里面还有三个重要的方法:

    • addView(); 
    • updateViewLayout();
    • removeView();

在WindowManager中,addView方法表示的是将主窗口中的顶级view(也就是DecorView)添加到WindowManager中,并建立会话。接下来会详细介绍。我们先来看看Window

Window:

Window是android中的窗口,表示顶级窗口的意思,也就是主窗口,它有两个实现类,PhoneWindowMidWindow,我们一般的activity对应的主要是PhoneWindow,在activity中经常使用的setContentView等方法也是在这个里面实现的。

    @Override
    public void setContentView(View view,ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }

        mContentParent.addView(view, params);

        final Callback cb = getCallback();

        if (cb != null) {

           cb.onContentChanged();  //窗口类容发生变化时更新

        }

    }

 每个主窗口中都有一个View,称之为DecorView,是主窗口中的顶级view(实际上就是ViewGroup),在View中有两个成员变量叫做mParentmChildren,它是用来管理view的上下级关系的。而ViewGroup是对一组View的管理。因此,在ViewGroup中建立了所有view的关系网。而最终ViewGroup附属在主窗口上。这样就很容易在窗口中通过findViewById找到具体的View了。view中的事件处理也是根据这个路径来处理的。

我们再来看看ActivityThead中的两个重要的方法:

           performLaunchActivity( );

            handleResumeActivity( );

performLaunchActivity中,会调用activity.attach方法建立一个window, 在handleResumeActivity方法中启动activity的时候,会将主窗口加入到WindowManager

      View decor =r.window.getDecorView();  //获得窗口的顶级View
      decor.setVisibility(View.INVISIBLE);
      ViewManager wm= a.getWindowManager();    //WindowManager继承自ViewManager
      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);  //实际上是把主窗口的顶级view加入到WindowMangaer
      }

我们再来看看WindowManager。

WindowManager:

WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。

通过Context.getSystemService(Context.WINDOW_SERVICE)的方式可以获得WindowManager的实例.

WindowManager继承自ViewManager,里面涉及到窗口管理的三个重要方法,分别是:

    • addView(); 
    • updateViewLayout();
    • removeView();  

在WindowManager中还有一个重要的静态类LayoutParams.通过它可以设置和获得当前窗口的一些属性。

我们先来看看addView()方法,在addView中,会利用LayoutParams获得window的View属性,并为每个window创建ViewRoot,ViewRoot是View和WindowManager之间的桥梁,真正把View传递给WindowManager的是通过ViewRoot的setView()方法,ViewRoot实现了View和WindowManager之间的消息传递。在将主窗口添加到WindowManger时,它首先会建立一个代理对象:

   wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE)

并且打开会话(IWindowSession),之后Window将通过该会话与WindowManager建立联系,

来看下setView方法:

     try {

        res =sWindowSession.add(mWindow, mWindowAttributes,

         getHostVisibility(), mAttachInfo.mContentInsets);

     } catch (RemoteException e) {

         mAdded = false;

        mView = null;

         mAttachInfo.mRootView =null;

         unscheduleTraversals();

         throw newRuntimeException("Adding window failed", e);

      } finally {

         if (restore) {

            attrs.restore();

         }

      }

在这段代码中,ViewRoot通过IWindowSession把窗口添加到WindowManager中。ViewRoot继承了Handler,实际上它的本质就是一个Handler,窗口中View的事件处理、消息发送、回调等将通过ViewRoot来处理。

这样就完成了把窗口添加到WindowManager中,并交由WindowManager来管理窗口的view、事件、消息收集处理等。

(本文参考了互联网上的一些资源,但是找不到地址,如有侵权,请及时联系,立即修改)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值