View的绘制流程

简介: View的绘制流程

view的绘制流程主要为measure,layout,draw三个阶段

View与window的逻辑结构如图所示:

ViewRootImpl(替代ViewRoot)类,是连接WindowMannager和DecorView的纽带,View的三大流程均是通过ViewRoot完成的,当activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,在ViewRootImpl里的performTraversals()进行分发


activity是系统可视化交互组件,四大组件都由AMS统一管理生命周期,事实上它的职责只是生命周期的管理,处于单一职责的原则,那势必需要将activity和其上的视图View进行解耦,那么就引入window的概念,它是个抽象类,对于activity来说,它的具体实现类是PhoneWindow,在activity执行attach的时候,会创建一个PhoneWindow对象,PhoneWindow作为装载根视图DecorView的顶级容器,


activity通过setContentView实际上是调用了PhoneWindow来创建DecorView,并解析xml布局加载到DecorView的contentView部分。

 

在activityThread 调用lauchactivity的时候,会调用activity的attach函数,在attach函数里面将view和activity绑定将window和wms进行绑定,绑定完成之后,才会调用,instrumentation的callactivity的Oncreate函数,这个时候才调用activity生命周期的onCreate函数,所以window和activity建立联系是在attch的时候,真正显示视图的是在onCreate方法里面的setContentView,这个时候才会把我们layout的xml布局加载到ContentView来

ViewRootImpl

ViewRootImpl的根本目的是用来管理整个View的流程,就是通过performTraversals这个方法

performTraversals方法,首先会调用performMeasure,这个方法会调用顶级view的measure函数

// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

子容器会重复调用父容器的measure方法,如此反复就完成了整个树的遍历,

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

view的measure函数会调用onMeasure函数 ,performLayout和performDraw,也亦是如此,唯一不同的是draw中调用的是dispatchDraw

 
    /**
     * <p>
     * This is called to find out how big a view should be. The parent
     * supplies constraint information in the width and height parameters.
     * </p>
     *
     * <p>
     * The actual measurement work of a view is performed in
     * {@link #onMeasure(int, int)}, called by this method. Therefore, only
     * {@link #onMeasure(int, int)} can and must be overridden by subclasses.
     * </p>
     *
     *
     * @param widthMeasureSpec Horizontal space requirements as imposed by the
     *        parent
     * @param heightMeasureSpec Vertical space requirements as imposed by the
     *        parent
     *
     * @see #onMeasure(int, int)
     */
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

draw的流程

/*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      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)
         */
// Step 3, draw the content
onDraw(canvas);
 
// Step 4, draw the children
dispatchDraw(canvas);

 


如图所示,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,这三个方法分别完成顶级View的measure、layout、draw这个三个流程。其中:


1.perfromMeasure中会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程。接着子元素会重复父元素的measure过程,如此反复就完成整个View树的遍历。


2.performLayout的传递流程和performMeasure是一样的。


3.performDraw的传递过程是在draw方法中通过dispathDraw来实现的,本质上并没有区别。


Measure过程决定了View的宽高,Measure完成以后,可以通过getMeasuredWidth和getMeasuredHeight方法来获取到View测量后的宽高,在几乎所有的情况下它都等于View的最终宽高,这仅仅是在代码规范的前提之下。


layout最终决定了View的四个顶点的坐标和实际View的宽/高,完成以后,可以通过getTop、getBottom、getLeft、getRight来拿到View的四个顶点坐标位置,并可以通过getWidth和getHeight来得到View的最终宽高


draw过程决定了View的显示,只有draw方法完成以后View的内容才会最终显示在屏幕上


draw过程


1.绘制背景background.draw(canvas)


2.绘制自己(ondraw)


3.绘制children(dispatchDraw)


4.绘制装饰(onDrawScrollBars)

 

目录
相关文章
|
Android开发 开发者
Android 13 NotificationChannels与Notification的加载流程
Android 13 NotificationChannels与Notification的加载流程
1662 0
Android 13 NotificationChannels与Notification的加载流程
|
Linux 测试技术 Android开发
Linux下Android Studio 3.6以及最新版安装图文详解
Linux下Android Studio 3.6以及最新版安装图文详解
959 0
|
Android开发 数据安全/隐私保护 容器
Android10.0 SystemUI—锁屏加载分析
Android10.0 SystemUI—锁屏加载分析
2143 0
Android10.0 SystemUI—锁屏加载分析
|
存储 安全 数据安全/隐私保护
轻松解锁BitLocker:创建自动解密批处理脚本
本文介绍了如何在Windows 7中通过创建批处理脚本来简化BitLocker驱动器的解锁过程。利用`manage-bde`命令行工具,用户可以通过输入驱动器盘符和密码来解锁BitLocker保护的驱动器,同时强调了安全注意事项,建议避免在脚本中直接写入密码以防止安全隐患。
816 1
|
SQL 存储 分布式计算
Hive精选10道面试题
Hive精选10道面试题
1040 3
Hive精选10道面试题
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
532 3
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
1650 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
Java 开发工具 Android开发
如何访问 android系统hide的类或接口
如何访问 android系统hide的类或接口
972 1
|
API 开发工具 Android开发
调用Android原生@SystemApi、@Hide方法
调用Android原生@SystemApi、@Hide方法
1370 1
|
缓存 Android开发 开发者
Dagger2和它在SystemUI上的应用
Dagger2和它在SystemUI上的应用
Dagger2和它在SystemUI上的应用