版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
目录9.1ViewRoot和DecorView9.2理解MeasureSpec9.3View的工作流程9.4自定义View
九、View的工作原理九、View的工作原理ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewGroup对象,并将ViewRootImpl对象和DecorView建立关系。View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽高,layout用来确定View在父容器中的放置位置,而draw则负责将View绘制在屏幕上。针对performTraversals的大致流程,如图9.1所示。9.1ViewRoot和DecorView九、View的工作原理图9.1九、View的工作原理performTraversals会移除调用performMeasure、performLayout和performDraw,这三个方法分别完成顶级View的measure、layout和draw这三大流程,其中在performMeasure中会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素了,这样就完成了一次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。同理,performLayout和perform的传递流程和performMeasure是类似的,唯一不同的是,performDraw的传递过程是在draw方法中通过dispatchDraw来实现的。九、View的工作原理Measure过程中决定了View的宽高,Measure完成之后,可以通过getMeasureWidth和getMeasureHeight方法来获得View的测量宽高,在几乎所有的情况下它都等同于View的最终宽高,特殊情况除外。Layout过程决定了View的四个顶点的坐标和实际的View的宽高,完成以后,可以通过getTop、getBottom、getLLeft、getRight来得到View的四个顶点的位置,并可以通过getWidth和getHeight来得到View的最终宽高。Draw过程则决定了View的显示,只有Draw方法完成以后View的内容才能呈现在屏幕上。九、View的工作原理
如图9.2所示,DecorView作为View,一般情况下它内部包含了一个竖直方向的LineraLayout,在这个LinearLayout里面有上下两个部分,上面为标题栏,下面为内容栏。在Activity中我们通过setContentView所设置的布局文件其实就是被加到内容栏中去了。图9.2九、View的工作原理MeasureSpec代表一个32位int值。高2位代表SpecMode,低30位代表SpecSize。SpecMode是指测量模式,而SpecSize指在某种测量模式下的规格大小。9.2理解MeasureSpec9.2.1MeasureSpec九、View的工作原理九、View的工作原理
MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配。为了方便操作,其提供了打包和解包操作,SpecMode和SpecSize也是一个int值。SpecMode有三类:。
UNSPECIFIED:父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
EXACTLY:父容器已经检测出View所需要的精确大小,这个时候最终大小就是SpecSize所指定的值,它对应于LayoutParams中的matchparent和具体的数值两种模式。
AT_MOST:父容器指定了一个可用的大小即SpecSize,View的大小不能大于这个值,具体是多大看不同的View的具体实现。它对应于LayoutParams中的wrap_content。九、View的工作原理
在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定View测量后的宽高。
对于DecorView来说,在ViewRootImpl(注意这个类是隐藏的,需要在resoure文件中去找)中的measureHierarchy方法中有如下一段代码,它展示了DecorView的MeasureSpec的创建过程,其中desiredWindowWidth和desiredWindowHeight是屏幕的尺寸。9.2.2MeasureSpec和Layoutparams的对应关系九、View的工作原理
九、View的工作原理DecorView的MeasureSpec的产生过程遵守了如下规则,根据它的LayoutParams中的宽高的参数来划分。
LayoutParams.MATCH_PARENT:精确模式,大小就是窗口的大小;
LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超过窗口的大小;
固定大小:精确模式,大小为LayoutParams中指定的大小。
对于普通的View来说,这里是指我们布局中的View,View的measure过程由ViewGroup传递而来,ViewGroup的measureChildWithMargins方法:九、View的工作原理
上述方法会对子元素进行measure,在调用子元素的measure方法之前会先通过getChildMeasureSpec方法得到资源的MeasureSpec。从代码来看,很显然,子元素的MeasureSpec的创建与父容器的MeasureSpec和子元素自身的LayoutParams有关,此外还和View的margin和padding有关。具体看ViewGroup的getChildMeasureSpec方法。如下所示:九、View的工作原理
九、View的工作原理九、View的工作原理九、View的工作原理
上述方法主要作用是根据父容器的MeasureSpec同时结合View本身的LayoutParams的参数来确定子元素的MeasureSpec,参数中的padding是指父容器已占用的控件大小,因此子元素可用的大小为父容器的尺寸减去padding,具体的代码如下:getChildMeasureSpec清楚地展示了普通View的MeasureSpec的创建规则。表9.1对getChildMeasureSpec的工作原理进行了梳理。表中的ParentSize是指父容器中目前可用的大小。九、View的工作原理childLayoutParamsparentSpecModeEXACTLYAT_MOSTUNSPECIFIEDdp/pxEXACTLYchildSizeEXACTLYchildSizeEXACTLYchildSizeMatch_parentEXACTLYparentSizeAT_MOSTparentSizeUNSPECIFIEDoWarp_contentAT_MOSTparentSizeAT_MOSTparentSizeUNSPECIFIEDo表9.1普通View的MeasureSpec的创建规则
只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以快速地确定子元素的MeasureSpec了,有了MeasureSpec就可以进一步确定出子元素测量的大小了。九、View的工作原理
1.View的Measure过程View的Measure过程由其Measure方法来完成。Measure方法是一个final类型的方法,意味着子类不能重写此方法。在View的Measure方法中会去调用View的onMeasure方法,因此只需要看onMeasure的实现即可。View的onMeasure方法如下:9.3View的工作流程9.3.1Measure过程九、View的工作原理
getDefaultSize方法:九、View的工作原理getDefaultSize返回的大小就是measureSpec中的specSize,而这个specSize就是View测量后的大小。这里多次提到测量后的大小,是因为View最终的大小是在layout阶段确定的,所以这里必须要加以区分,但不是所有情况下的View的测量大小都与最终大小相等。UNSPECIFIED一般用于系统内部的测量过程,在这种情况下,View的大小为getDefaultSize的第一个参数,即宽高分别为getSuggestedMinimumWidth和getSuggestedMinimumHeight这两个方法的返回值,看一下它们的源码:九、View的工作原理
可以看出,如果View没有设置任何背景,那么View的宽度为mMinWidth,而mMinWidth对应于android:minWidth这个属性所指定的值,因此View的宽度即为android:minWidth属性所指定的值。这个属性如果不知道,那么mMinWidth则默认为0;如果View指定了背景,则View的宽度为max(mMinWidth,mBackground.getMinimumWidth())。我们看一下Drawable的getMinimumWidth()方法,如下所示:九、View的工作原理
可以看出,getMinimumWidth返回的就是Drawable的原始宽度,前提是这个Drawable有原始宽度,否则就返回0。那么Drawable在什么情况下有原始宽度呢?ShapeDrawable无原始宽高,而BitmapDrawable有原始宽高。
从getDefault方法的实现来看,View的宽高由SpecSize决定,所以我们可以得出以下结论:直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身参数,否则在布局文件中使用wrap_content就相当于match_parent。九、View的工作原理
2.ViewGroup的measure过程
对于ViewGroup来说,除了完成自己的measure过程以外,还会去调用所有子元素的measure方法,各个子元素再递归去执行这个过程。和View不同的是,ViewGroup是一个抽象类,因此它没有重写View的onMeasure方法,但是它提供了一个叫measureChildren的方法:九、View的工作原理ViewGroup在measure时会对每一个子元素进行measure,measureChild方法的实现:measureChild就是取出子元素的LayoutParams,然后再通过getChildMeasureSpec来创建子元素的MeasureSpec,接着MeasureSpec直接传递给View的measure方法来进行测量。九、View的工作原理
不同的ViewGroup子类有不同的布局特性,这导致它们的测量细节都不相同。LinearLayout的onMeasure方法:
上述代码对不同的布局方向做了不同的测量,选择查看垂直布局的LinearLayout测量过程,即measureVertical方法。measureVertical方法中重要代码如下:九、View的工作原理
系统会遍历子元素并对子元素执行measureChild-BeforeLayout方法,调用子元素的measure方法,各个子元素就开始依次进入measure过程,并且系统会通过mTotalLength这个变量来存储LinearLayout在竖直方向的初步高度。每测量一个子元素,mTotalLength就会增加包括子元素的高度以及子元素在竖直方向上的margin等。当子元素测量完毕后,LinearLayout会测量自己的大小,源码如下:九、View的工作原理
当子元素测量完毕后,LinearLayout会根据子元素的情况来测量自己的大小,针对竖直的LinearLayout而言,它在水平方向的测量过程遵循View的测量过程,在竖直方向的测量过程与View有所不同。最终高度还需要考虑其他在竖直方向的padding,这个过程可以进一步参看源码:九、View的工作原理九、View的工作原理如果在Activity已启动的时候就做一个任务,但是这个任务需要获取这个View的宽高。实际上在onCreate或者onStart、onResume中均无法正确得到某个View的宽高信息,这是因为View的measure和Activity的生命周期方法不是同步执行的,一次无法保证Activity执行了onCreate、onStart、onResume时某个View已经测量完毕。如果View还没有测量完毕,那么获得的宽高就是0。给出四种方法来解决这个问题。九、View的工作原理1) onWindowFocusChangedView已经初始化完毕了,宽高已经准备好了,这个时候去获取宽高没有问题。onWindowFocusChanged会被调用多次,当Activity的窗口得到焦点和失去焦点时均会被调用一次。当Activity继续执行和暂停执行时,onWindowFocusChanged均会被调用,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用。2) View.post(runnable)
通过post可以将一个runnable投递到消息队列的尾部,然后等待Looper调用此runnable的时候,View也已经初始化了。典型代码如下:九、View的工作原理3) ViewTreeObserver
使用ViewTreeObserver的众多回调方法可以完成这个功能,比如使用onGlobalLLayoutListener这个接口,当View树的状态发生改变或者View树内部的View可见性发生改变时,onGlobalLyaout方法将被回调,因此这是获取View宽高的一个很好的时机。需要注意的是,伴随View树的状态改变等,onGlobalLayout会被调用多次。典型代码如下:九、View的工作原理九、View的工作原理Layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用其Layout方法。在Layout方法中onLayout方法会被调用。Layout过程比measure过程简单,在Layout方法中确定View本身的位置,而onLayout方法则会确定所有子元素的位置。View的Layout方法如下所示:9.3.2Layout过程九、View的工作原理九、View的工作原理
Layout方法的大致流程:首先通过setFrame方法来设定View的四个顶点的位置,即初始化mLeft、mRight、mTop、mBottom这四个值。View的四个顶点一旦确定,那么View在父容器中的位置就确定了,接着会调用onLayout方法。这个方法的用途是父容器确定子元素的位置,和onMeasure方法类似,onLayout的具体实现同样和具体的布局相关,那么View和ViewGroup均没有真正实现onLayout。接下来,我们可以看一下LinearLayout的onLayout方法,如下所示:九、View的工作原理
LinearLayout中的Layout的实现逻辑和onMeasure的实现逻辑类似:九、View的工作原理九、View的工作原理
此方法会遍历所有子元素并调用setChildFrame方法来为子元素指定对应的位置,其中childTop会慢慢增大,这就意味着后面的子元素会被放置在靠下的位置,这刚好符合竖直方向的linearLayout的特性。至于setChildFrame,它仅仅是调用子元素的layout方法而已,这样父元素在子元素layout方法中完成自己的定位以后,就通过onlayout方法去调用子元素的layout方法,子元素又会通过自己的layout方法来确定自己的位置,这样一层一层地传递下去就完成了整个View树的layout过程。setChildFrame方法的实现如下所示:九、View的工作原理
在setChildFrame中的width和height实际上就是子元素的测量宽高。九、View的工作原理
Draw过程的作用是将View绘制到屏幕上面。View的绘制过程遵循如下几步:(1)绘制背景background.draw(canvas)。(2)绘制自己(onDraw)。(3)绘制children(dispatchDraw)。(4)绘制装饰(onDrawScrollBars)。
当需要使用自定义View时,都需要实现该方法,9.3.3Draw过程九、View的工作原理
继承View主要用于实现一些不规则的效果,即这些效果不方便通过布局的组合方式来达到,往往需要静态或者动态地显示一些不规则的图形。很显然这需要通过绘制的方式来实现,即重写onDraw方法。采用这种方式需要自己支持wrap_content,并且padding也需要自己处理。9.4自定义View9.4.1继承View九、View的工作原理
定义一个MyCustomView类,该类继承了View:九、View的工作原理九、View的工作原理
在MyCustomView的构造方法中得到了background.png图片的位图对象,在onDraw方法里创建了一个paint对象,同时绘制一块矩形,该矩形的大小由viewWidth、viewHeight决定,viewWidth以及viewHeight在第一次加载MyCustomView时得到它们的初始值,也就是通过onSizeChanged()方法得到它的值。最后通过canvus.drawBitmap()把图片绘制到指定的区域。我们需要将该View放入布局文件中去,Activity_main.xml代码如下:九、View的工作原理九、View的工作原理
在布局文件中部署该自定义View的时候,需要将包名以及类名都写进去。如果仅仅是写入MyCustomView将会出现错误。
运行程序,出现如这样的结果:图9.3九、View的工作原理
如图9.3所示,在textView的区域下面绘制了一块红色区域,在该区域的左上角有一个图片,即background.xml,这样简单地实现了自定义View。接下来我们将在此基础上更加复杂化。我们希望上图中那块灰色的区域能够填充整个红色区域:九、View的工作原理九、View的工作原理
在修改部分,使用双层for循环,在外层循环,绘制图片的宽度,内层循环绘制图片的高度。
执行上述修改后的代码可以看到,整个区域都已经填充该图片。接下来,我们将在这些图片上绘制图9.4,并且居中。在以往的布局文件中,我们可以通过控件的属性设置控件的位置。在自定义控件内,没有这些属性设置,需要我们自己去计算。图9.4九、View的工作原理
计算这个自定义View的中间位置,在onDraw()方法中添加如下代码:九、View的工作原理
整个过程共分为三步:首先需要得到该图片的位图对象;同时需要绘制的坐标信息;当有了上述两个条件之后,通过canvus方法就可绘制图片、显示图片上的文字了。类似于Android自定义的TextView,运行结果:图9.5九、View的工作原理
如图9.5所示,在自定义View上加载了“疯狂英语”文字,接下来,动态的显示文字,在上述demo中,在界面的底部逐个显示一行字。文字图片如图:图9.6九、View的工作原理
想要“随时随地学英语”这几个字慢慢地显示出来,而不是一下全部显示出来,这样就形成了一个动态的效果。在传统的控件中,TextView无法实现该效果,而在自定义View中可以完成:九、View的工作原理运行程序:图9.7九、View的工作原理
结果如图9.7所示,动态文字内容显示出来,继续修改,用到线程:九、View的工作原理
让MyCustomView实现Runnable接口,定义一个图片显示区域控制模块参数index。利用线程刷新index参数,从而控制图片显示区域。让图片中的文字达到慢慢显示的后果。线程代码如下:九、View的工作原理
在主线程刷新UI使用的是Invalidate()方法,在此案例中index的变化是在子线程中,故使用的是postInvalidate();。
在MyCustomView的构造方法中开启线程,代码如下:九、View的工作原理
当加载该自定义View时,开启工作线程,改变index值的大小,同时调用postInvalidate()方法刷新UI,每次刷新UI时都将重新调用onDraw()方法,从而动态显示文字。在onDraw()中增加代码如下:九、View的工作原理案例:使用自定义View来实现一个复杂的控件——绘制股票的趋势图。1.实时股价-时间界面
一个股票趋势图包含有时间、价格以及趋势走向等因素,我们一个一个来完成它。首先完成在屏幕下方绘制时间。模拟一组数据用于绘制时间。
自定义View类代码如下:九、View的工作原理九、View的工作原理九、View的工作原理
在自定义View类中,我们定义了一个集合lists保存股票价格点,在lists中保存的是HashMap对象。之所以用到HashMap,是由于股票价格点同时包含了时间及价格。在自定义类还自定义了setData()方法,该方法用于设置股票价格数据。在onSizeChanged()方法中得到自定义控件的长度。最好在onDraw()方法中绘制时间。
注意:在每个方法中都利用Log做了日志输出,当运行程序时可以更直观地看到程序运行的大致方法调用过程。MainActivity.java文件代码如下:九、View的工作原理九、View的工作原理
九、View的工作原理Activity_main.xml代码如下:九、View的工作原理运行结果:图9.8九、View的工作原理2.实时股价-价格界面
我们需要知道股价最大值,只有根据股价的最大值,才能得到每一时间点股价与最大值的比例,从而计算出该点在自定义View中的位置,所以首先我们需要计算出这些时刻点的最大股价。九、View的工作原理九、View的工作原理九、View的工作原理九、View的工作原理九、View的工作原理运行程序:图9.10九、View的工作原理
3.实时股价-动态展示界面
如图9.10所示,股票的时间点、价格、连线都已经连接起来了。如何让数据动态的显示,是我们接下来的任务。在上面的代码中,数据来源于MainActivity的onCreate()方法,固定的数据,我们需要动态显示它。此处用到的是Random对象。在getDate()中修改代码如下:九、View的工作原理九、View的工作原理
通过调用Random对象的nextInt(n)对象,来产生一个大于等于0小于n的随机数,这样就实现了动态数据。在真实的项目中,用到的是网络数据,需要在MyCustomView显示:九、View的工作原理九、View的工作原理运行程序,出现动态的实时股市图:图9.11九、View的工作原理
这种方法主要用于实现自定
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 徐州工程学院《服装结构与工艺(二)》2021-2022学年第一学期期末试卷
- 信阳师范大学《写意花鸟临摹》2022-2023学年第一学期期末试卷
- 信阳师范大学《人工智能》2023-2024学年第一学期期末试卷
- 《机械零件加工》课件教学日历
- 西南医科大学《卫生政策与管理》2023-2024学年第一学期期末试卷
- 西南林业大学《家具造型设计》2023-2024学年第一学期期末试卷
- 西京学院《放射物理与防护》2022-2023学年第一学期期末试卷
- 西昌学院《程序设计》2021-2022学年第一学期期末试卷
- 西北大学《数据结构实验》2022-2023学年第一学期期末试卷
- 第十单元跨学科实践活动9探究土壤酸碱性对植物生长的影响教学设计-2024-2025学年九年级化学人教版(2024)下册
- 质量环境管理手册+程序文件+表单全套(格式可转换)联
- 医疗风险防范培训培训课件
- 消化道早癌内镜诊断与治疗
- 小学数学-《20以内进位加法和退位减法整理复习》教学设计学情分析教材分析课后反思
- 人工智能歧视的法律治理
- 林州市房地产市场调研报告
- 2023-2024学年高中政治统编版必修二3-2 推动高质量发展 第2课时 教案
- 物流园区运营合作协议
- 星级班主任评比方案
- 客户投诉处理控制程序(含表格)
- 证照使用借阅申请表
评论
0/150
提交评论