携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第二十二天,点击查看活动详情
重写Android之View的工作原理系列(一)
前言
本系列主要介绍两个方面,首先介绍View的工作原理,懂了原理后我们在来实现自定义View。
我们知道在Android中,View是很重要的知识点,我们使用的TextView、ImageView等都是继承与View,当然这些都是Android的GUI库提供的基本控件,有时候并不能满足我们的日常开发,我们也需要自定义View,通过自定义View可以实现复杂的一些效果,所以为了我们更好的自定义view,我们需要先了解View的工作原理
ViewRoot和DecorView
我们了解一下几个基本的概念:
- Activity
Activity只是控制生命周期和处理事件,并不是控制视图显示,真正控制视图的Window。一个Activity包含一个Window,Window才是真的窗口。
- Window
Window是视图的承载器,内部持有一个DecorView,这个DecorView才是View的根布局。
Window是一个抽象类,实际在Acitiy中持有的是其子类PhoneWindow。PhoneWindow中一个内部类DecorView,通过DecorView来加载Activity中设置的布局。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
- DecorView
DecorView是FrameLayout的子类,它是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。我们可以看一下下面的具体布局结构:
- ViewRoot
所有View的绘制以及事件分发等交互都是通过它来执行或传递的。ViewRoot对应于ViewRootImlp类,它是链接WindowManager和DecorView的纽带,View的三大流程是通过ViewRoot来完成的。
View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和 高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。针对performTraversals的大致流程:
如上图所示,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,这三个方法分别完成顶级View的measure、layout和draw这三大流程。其 中在performMeasure中会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容 器传递到子元素中了,这样就完成了一次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。同理,performLayout和performDraw的 传递流程和performMeasure是类似的,唯一不同的是,performDraw的传递过程是在draw方法中通过dispatchDraw来实现的,不过这并没有本质区别。
measure过程决定了View的宽/高,Measure完成以后,可以通过getMeasuredWidth和getMeasuredHeight方法来获取到View测量后的宽/高,在几乎所有的情况下它都等同于 View最终的宽/高,但是特殊情况除外,这点在本章后面会进行说明。