Android中View绘制流程的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。setContentView整个过程主要是如何把Activity的布局文件或者java的View添加至窗口里,重点概括为:创建一个DecorView的对象mDecor,该mDecor对象将作为整个应用窗口的根视图。依据Feature等style theme创建不同的窗口修饰布局文件,并且通过findViewById获取Activity布局文件该存放的地方(窗口修饰布局文件中id为content的FrameLayout)。将Activity的布局文件添加至id为content的FrameLayout内。当setContentView设置显示OK以后会回调Activity的onContentChanged方法。Activity的各种View的findViewById()方法等都可以放到该方法中,系统会帮忙回调。view绘制主要包括三个方面:measure 测 香港云主机量组件本身的大小layout 确定组件在视图中的位置draw 根据位置和大小,将组件画出来视图绘制的起点在ViewRootImpl类的performTraversals()方法,该方法完成的工作主要是:根据之前的状态,判定是否重新计算测试视图大小(measure)、是佛重新放置视图位置(layout)和是否重新重绘视图(draw),部分源码如下:几乎所有的组件都是继承View类的,而关于view的测量工作,日常开发用得多的方法就是measure和onMeasure两个方法,measure不可重写,当我们自定义时主要重写onMeasure方法即可,在方法内部我们必须完成组件的mMeasuredWidth和mMeasuredHeight实际尺寸测量,而这个尺寸是需要父视图和子视图共同决定的measure流程从根视图measure遍历整个view树结构,如下:
这里写图片描述还要注意视图尺寸MeasureSpec是一个组合尺寸,它是一个32位bit值,高两位是尺寸模式specMode,低30位是尺寸大小值,我们可以利用提供的原声库方法很方便的进行尺寸组合和拆解:
specMode有三种: MeasureSpec.EXACTLY表示确定大小, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不确定而在视图测量meause中,父组件传给子组件的一般都是一个组合尺寸,我们可以拿出具体尺寸然后根据其他条件产生一个新的尺寸值,将这个值用setMeasuredDimension设置mMeasuredWidth和mMeasuredHeight具体尺寸,完成测量;MeasureSpec(View的内部类)测量规格为int型,值由高2位规格模式specMode和低30位具体尺寸specSize组成。其中specMode只有三种值:View的measure方法是final的,不允许重载,View子类只能重载onMeasure来完成自己的测量逻辑。最顶层DecorView测量时的MeasureSpec是由ViewRootImpl中getRootMeasureSpec方法确定的(LayoutParams宽高参数均为MATCH_PARENT,specMode是EXACTLY,specSize为物理屏幕大小)。ViewGroup类提供了measureChild,measureChild和measureChildWithMargins方法,简化了父子View的尺寸计算。只要是ViewGroup的子类就必须要求LayoutParams继承子MarginLayoutParams,否则无法使用layout_margin参数。View的布局大小由父View和子View共同决定。使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。layout的流程主要也是遍历整个view树结构,调用view.layout(int l, int t, int r, int b)确定好view的具体坐标位置,流程图如下
这里写图片描述当我们自定义一个组件时,通常时重写onLayout方法,里面实现好自己的逻辑,最后在调用layout方法完成视图位置确定,如果自定义组件时一个ViewGroup的话,还需要我们去遍历每一个child确定尺寸整个layout过程比较容易理解,从上面分析可以看出layout也是从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。具体layout核心主要有以下几点:View.layout方法可被重载,ViewGroup.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑。measure操作完成后得到的是对每个View经测量过的measuredWidth和measuredHeight,layout操作完成之后得到的是对每个View进行位置分配后的mLeft、mTop、mRight、mBottom,这些值都是相对于父View来说的。凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的(前面《Android应用setContentView与LayoutInflater加载解析机制源码分析》也有提到过)。使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值。完成measure和Layout后,ViewRootImpl中的代码会创建一个Canvas对象,然后调用View的draw()方法来执行具体的绘制工。所以又回归到了ViewGroup与View的树状递归draw过程
先来看下View树的递归draw流程图,如下:
这里写图片描述可以看见,绘制过程就是把View对象绘制到屏幕上,整个draw过程需要注意如下细节:如果该View是一个ViewGroup,则需要递归绘制其所包含的所有子View。View默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。View的绘制是借助onDraw方法传入的Canvas类来进行的。区分View动画和ViewGroup布局动画,前者指的是View自身的动画,可以通过setAnimation添加,后者是专门针对ViewGroup显示内部子视图时设置的动画,可以在xml布局文件中对ViewGroup设置layoutAnimation属性(譬如对LinearLayout设置子View在显示时出现逐行、随机、下等显示等不同动画效果)。在获取画布剪切区(每个View的draw中传入的Canvas)时会自动处理掉padding,子View获取Canvas不用关注这些逻辑,只用关心如何绘制即可。默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。请求重新绘制视图,调用drawinvalidate在主线程调用postInvalidate是在非主线程调用requestLayout()方法会调用measure过程和layout过程,不会调用draw过程,也不会重新绘制任何View包括该调用者本身。看完上述内容,你们掌握Android中View绘制流程的原理是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注开发云行业资讯频道,感谢各位的阅读!
据我所知,优秀的程序员都热爱写作(不止是狭义上的写博客)。如果你能举出一些例外,请一定转告我。为什么这么说呢?一个优秀的程序员想要分享一段开源的代码,假如别的程序员看不懂的话,他的挫败感一定会油然而生,除非他不想让别人看懂,但我觉得这样的话也没必要开源了吧。为…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。