View的绘制过程分析
2016-05-24 20:43
489 查看
写在前面:
最近在学习《Android开发艺术探索》关于View的工作原理的内容,虽然讲解的很细致,但总觉得仅仅看一遍还是难以对View的整体绘制过程有较为明晰的认识。接下来,我根据简单的代码,重点观察分析View的measure– layout – draw 绘制过程。
关于这个问题,可参考http://www.cnblogs.com/jerehedu/p/4679534.html。Acitvity启动过程中,追溯其源码,可得出下图所示的调用过程:
Activity在启动过程中会调用主线程ActivityThread的handleResumeActivity方法,此方法会将DecorView和WindowManagerImpl对象关联起来。最终,ActivityThread中生成的DecorView经过WindowManagerImpl、WindowManagerGlobal,最终调用了ViewRootImpl中的setView方法,将DecorView设置赋值给了ViewRootImpl中的mView属性。在ViewRootImpl中调用的performTraversals方法将会开启View的整个绘制过程。
2)View绘制过程深入
measure过程:
1.View的measure方法为final类型,不能被重写。在measure方法中会调用onMeasure方法,因而一般重写onMeasure方法实现自定义View。
2.View的onMeasure方法本身设定View的测量后大小为android:minWidth属性所设置的值,此属性默认为0。如果为View指定了背景,则View的测量后大小为背景图片的原始尺寸。另外,直接继承View的控件需要重写onMeasure方法设置wrap_content时的自身大小,否则其效果等同于match_parent.
3.对于ViewGroup实例,比如LinearLayout、RelativeLayout等,不仅要根据自己的布局特性重写onMeasure方法还要实现对子元素进行measure的方法。
4.view的大小取决于:父容器的MeasureSpec、父容器的padding以及view本身的LayoutParams。
5.measure过程可能要进行多次才能确定最终的测量宽/高,因而在measure过程中去获取view的宽/高可能会不准建议在onLayout方法中获取。
6.view的measure过程和Activity的生命周期方法并不同步。不能保证某一个生命周期阶段measure确定完成。
layout过程:
1.layout方法确定View本身的位置,onLayout方法确定所有子元素的位置。
2.onLayout的具体实现和具体布局有关,所以View和ViewGroup都只提供了空方法,需要子类重写。
3.对ViewGroup实例,在onLayout方法中会遍历子元素调用其layout方法。
4.在onLayout方法中需要对LayoutParameters.margin进行处理。
draw过程:
View.draw()分为以下几步:
(1)background.draw():绘制背景;
(2)onDraw():绘制自己;
(3)dispatchDraw():绘制子元素;
(4)onDrawScrollbars:绘制装饰。
1.自定义View一般会重写onDraw方法,需要处理padding。
2.dispatchDraw方法实现draw事件的一层层传递。
3.自定义View时序注意View中的setWillNotDraw方法。此方法为优化标志位。
setWillNotDraw(true)表示view不需要绘制任何内容,系统会进行响应优化。
默认情况下,View未启动此标志位,ViewGroup启动此标志位。
当自定义控件继承自ViewGroup时且需要绘制内容时需显式关闭此优化标记位。
最近在学习《Android开发艺术探索》关于View的工作原理的内容,虽然讲解的很细致,但总觉得仅仅看一遍还是难以对View的整体绘制过程有较为明晰的认识。接下来,我根据简单的代码,重点观察分析View的measure– layout – draw 绘制过程。
一、xml文件
其中,RevealLayout本质上就是LinearLayout。MyButton本质上就是Button。这里是为了方便观察绘制过程,对相关方法添加了打印信息。<com.ryg.chapter_4.ui.RevealLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="12dp" tools:context="${relativePackage}.${activityClass}" > <com.ryg.chapter_4.ui.MyButton android:id="@+id/button1" style="@style/AppTheme.Button.Green" android:onClick="onButtonClick" android:text="View Test" /> </com.ryg.chapter_4.ui.RevealLayout>
二、 运行结果
如下图,就只显示一个简单的Button,重点是对其绘制过程的观察。三、View绘制过程方法调用
控件构造器:RevealLayout()、MyButton() → RevealLayout().onMeasure→ RevealLayout().measureChildWithMargins→ MyButton.onMeasure → RevealLayout().onLayout → MyButton.layout → MyButton.onLayout → RevealLayout().draw → RevealLayout().onDraw → RevealLayout().dispatchDraw → MyButton.draw → MyButton.onDraw四、View绘制过程分析
1) View的绘制过程从何处开始呢?关于这个问题,可参考http://www.cnblogs.com/jerehedu/p/4679534.html。Acitvity启动过程中,追溯其源码,可得出下图所示的调用过程:
Activity在启动过程中会调用主线程ActivityThread的handleResumeActivity方法,此方法会将DecorView和WindowManagerImpl对象关联起来。最终,ActivityThread中生成的DecorView经过WindowManagerImpl、WindowManagerGlobal,最终调用了ViewRootImpl中的setView方法,将DecorView设置赋值给了ViewRootImpl中的mView属性。在ViewRootImpl中调用的performTraversals方法将会开启View的整个绘制过程。
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; …… performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); …… performLayout(lp, desiredWindowWidth, desiredWindowHeight); …… performDraw(); …… }
2)View绘制过程深入
measure过程:
1.View的measure方法为final类型,不能被重写。在measure方法中会调用onMeasure方法,因而一般重写onMeasure方法实现自定义View。
2.View的onMeasure方法本身设定View的测量后大小为android:minWidth属性所设置的值,此属性默认为0。如果为View指定了背景,则View的测量后大小为背景图片的原始尺寸。另外,直接继承View的控件需要重写onMeasure方法设置wrap_content时的自身大小,否则其效果等同于match_parent.
3.对于ViewGroup实例,比如LinearLayout、RelativeLayout等,不仅要根据自己的布局特性重写onMeasure方法还要实现对子元素进行measure的方法。
4.view的大小取决于:父容器的MeasureSpec、父容器的padding以及view本身的LayoutParams。
5.measure过程可能要进行多次才能确定最终的测量宽/高,因而在measure过程中去获取view的宽/高可能会不准建议在onLayout方法中获取。
6.view的measure过程和Activity的生命周期方法并不同步。不能保证某一个生命周期阶段measure确定完成。
layout过程:
1.layout方法确定View本身的位置,onLayout方法确定所有子元素的位置。
2.onLayout的具体实现和具体布局有关,所以View和ViewGroup都只提供了空方法,需要子类重写。
3.对ViewGroup实例,在onLayout方法中会遍历子元素调用其layout方法。
4.在onLayout方法中需要对LayoutParameters.margin进行处理。
draw过程:
View.draw()分为以下几步:
(1)background.draw():绘制背景;
(2)onDraw():绘制自己;
(3)dispatchDraw():绘制子元素;
(4)onDrawScrollbars:绘制装饰。
1.自定义View一般会重写onDraw方法,需要处理padding。
2.dispatchDraw方法实现draw事件的一层层传递。
3.自定义View时序注意View中的setWillNotDraw方法。此方法为优化标志位。
setWillNotDraw(true)表示view不需要绘制任何内容,系统会进行响应优化。
默认情况下,View未启动此标志位,ViewGroup启动此标志位。
当自定义控件继承自ViewGroup时且需要绘制内容时需显式关闭此优化标记位。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories