关于view的显示和绘制,不会去了解底层,仅仅在framework层做一些概述:以oo的思想,那么窗口的显示,内容的显示一定都有对应的类来相对应。了解了这一点之后,就去抽象一下android为了显示窗口而做的封装。
首先来说每一个activity一定有一个可供显示图像的东西,window,就是他抽象为一个window可以挂载所有view。在android中有一个实现类就是phonewindow,他实现了window的所有功能。那么可供挂载的view一定有一个根类,是decorview,这个是一个phonewindow的内部类。字面意义来看就是一个装饰类。他也仅仅就是一个view类,但是确实所有view类的根节点。然后我们使用xml布局的类或者是代码创建的类就可以挂载到这个view上了,还有一个东西叫viewroot,好像这个类才是根一样?这完全就是两个东西,viewroot其实是在windowmanager中!windowmanager又是他妈什么?这个和之前的activityThread和Instramentation的关系有些类似。window主要就是描述了window的所有功能,主要和我们的界面交互。windowManager是继承于viewmanager,主要就是操作view的添加view,删除view。viewroot是在windowmanager中,是为了和phonewindow交互,可以看一下继承体系,是继承与Handler也就说是主要处理消息的,viewroot处理的消息就是屏幕和键盘等等的一切交互动作。在UI线程进入消息循环之后,我们的交互就只是能通过外界的中断,而这些东西都由底层交给viewroot。然后在控制phonewindow,分发消息。进行界面view的各种显示和处理。
tip:上面的过程有点乱,简单来说我们可以代码操作的就是activity,activity里面有window对象,window对象负责的就是界面,window里面有一个windowManager和decorview。windowamanger负责在的decorview上添加view。也就是说window里面是没有addview方法的,只有利用windowmanager才可以添加或操作view。可以做一个实验,在oncreate中getWindowManager().removeView(getWindow().getDecorView());就是把decorview从window上删除,这个就是一个黑色背景,并且无法交互一会就会anr。这就说明decorview就是view的根。
说完代码操作的方式,就可以看一下底层对应的实际对象,window和windowmanager都是抽象类,decorview也使用的view的类型,在底层实际上是phonewindow ,windowmanagerimpl,decorview。然后这个些都只是view视图的显示,我们要交互使用viewroot。viewroot会和android系统的服务进程windowManagerService交互。进行把界面的各种消息添加到MessageQueue。viewroot就是一个Handler。
这个就是类层面的结构,可以看到抽象的都很好,使用oo思想,先来抽象每一个层面的对象,然后利用view和数据分离。window管理数据,windowManager处理view。
来看一下源码的调用流程:之前的创建activity的时候就使用了attach函数来把activity和Context挂在在了一起。这时候可以看到attach函数中做了什么?他创建了window对象,也就是phonewindow,他使用了polocyManager.makenewwindow,来创建了一个phonewindow,然后setWindowManager创建l了WindowManager。这个wm虽然看起来是LocalWIndowManager其实都是简单的类型包装,实际的就是windowManagerImpl。之后就是setcontentview中去添加了view,然后在onresume后调用activity的makeVisiable使得view绘制出来。
tip:实际上这里有一个问题:就是在oncreate之前是attach函数添加了phonewindow对象,但是在这之后直到oncreate并没有显示phonewindow的代码,但是你注意观察的话就会发现,在oncreate之前实际上界面上就有了decorview的布局了(后面具体说)。为什么?这感觉不合理,也就是在onstart之后按理说才是界面显示器。我现在都有疑问(未解决)。个人感觉这个界面显示是指的我们的布局view被加到了decroview中,并不是指的界面动作。生命周期函数有诸多的和用户界面不一致,其实更合理的是代码运行和生命周期是一致的。回到问题,其实源代码中开启activity要从上一篇讲的activityStack的realstartActivityLocked中调用的,这个里面有一个windowManagerService调用了windowManger的setAppvisiablity。这我感觉就是我们程序的显示。之后才发消息给ActivityThread去显示activity。总之这个过程很复杂。了解这些过程有助于我们以后的开发,但是要彻底了解还有很长的路要走。
了解了上面之后,研究的主线就清楚了,1,view如何添加到decorview? 2view 是如何绘制的?
1.view的解析和添加:android中view有两种方法创建:代码和xml。一般来说代码创建的时候调用的是一个参数的构造函数,而xml布局要使用两个参数的构造,这是为什么?多出来的参数就是AttributeSet 也就是在xml中的熟悉级,他要利用这个来进行解析转化为本身的属性值。这一切的过程都是从setcontentview开始的,phonewindow调用自己的setcontentview。里面调用了installDecor来创建了decorview。
tip:这时候就要分析decorview到底是是什么了,decorview实际上里面有一个LinearLyout,第一个元素为titleBar,现在为actionBAr。然后第二个元素就是frameLayout,也就是我们的布局的父类布局。setcontentview就是把布局放到这个framelayout里面。所有的解析过程都在insatlldecor;里面。
生成了decroview之后就是为mContentParent(framelayout)设置内容了,也就是解析xml。一般使用解析都是使用LayoutInflate。有很多生成方式,使用Context的getsystemservice,或者调用static函数from,或者直接View.Inflate,其实都是一样的,有了LayoutInfalte就调用inflate来解析view布局。
tip:这个时候要了解几个技巧和概念:
1megre和include标签,include是为了布局复用的标签,这个很好了解,但是megre呢?实际是为了消除冗余的父类布局,也就是说当使用include引入一个布局之后,可能会出现两个父类布局重复,这个时候必然是冗余了,可以将include的父类布局换成megre,解析的时候自动就会无视megre,这个实际上是在代码xml期间的标签。
2.要注意的就是viewGroup,我们知道view是一个树结构,存的时候实际就是每一个viewgroup存储自己的孩子在一个数组中view[] mChildren,无论是便利还是添加都是通过这个数组的。
具体的解析过程在inflate里面,inflate是解析根布局标签,然后交给rinflate去循环解析子类的标签,解析之后又createViewFromTag就是生成相应的对象,调用createView反射了对应类,并且调用了构造函数,这里就是使用的两个参数的构造,arg0是Context,arg1是attr就是xml属性集合。然后根据生成的view添加到viewgoup中,最后添加到decroview中。到此为止,view的解析就完成了。
2view如何绘制?
要找到这个过程实际上要到viewroot中去寻找,几乎所有的和窗口的动作都是由系统服务windowMangerService来执行的,而和他交互的就是viewroot,viewroot里面管理了view的显示和事件等动作。当要显示布局的时候,activity的makeVisiable最终调用到了viewroot的scheduleTraversals,然后加到了消息队列中处理之后调用了perfromTraverals,英语就是遍历的意思,在这个函数中执行了view的3个绘制过程在300多行的代码过程中调用了measure,layout,draw这也就是为什么要将这3个过程称之为view绘制流程。
measure是测量,也就是测量出每一个view的大小,lyout就是布局,布局也就是要把每一个view位置明确了,draw就是绘制,也就是表现到视图上,其实就是在一个bitmap的画布上画出各个控件来。
具体的绘制在下一篇绘制中叙述,所以综上就是我们应该了解的view的概述,其中,我们抽象的思想和如何去在一个界面上展现view的思想应该是我们关注的,以及很多的源代码都要认真学习。以前一直觉的自己很熟悉android,现在看来真是讽刺,android系统了不起,以前真实太无知了。了解更多之后,你就更加的知道自己的渺小。