对 ViewGroup 生命周期执行顺序的理解
2016-03-26 10:51
501 查看
生命周期在 Android 开发中是非常重要的内容,在学习自定义 ViewGroup 时,生命周期也必不可少。本文就从执行顺序角度,阐述一下自己的观点。
首先,自定义类
分别在这三种情况下输出日志:
在 activity_main.xml 中定义控件。
在 MainActivity 中
直接在 MainActivity
可以得出的结论是
直接 new ,不添加到任何父布局,只会调用构造方法
onFinishInflate 只会在从 xml 中加载时调用,并且只调用一次。
初始化 ViewGroup 的流程大致为:构造方法创建对象->从布局加载(xml中定义时)->第一遍测量->开始改变尺寸->第一遍布局->第二遍测量->第二遍布局
从 xml 加载第一遍测量和第二遍测量,都会调用两次 onMeasure(值得注意)
将上面的日志改为:
(1). 在 activity_main.xml 中定义控件。
(2). 在 MainActivity 中
可以得出如下结论:
getMeasuredWidth(): 只要一执行完 setMeasuredDimension() 方法,就有值了,并且不再改变。
getWidth():必须执行完 onMeasure() 才有值,可能发生改变。
如果 onLayout 没有对子 View 实际显示的宽高进行修改,那么 getWidth() 的值 == getMeasuredWidth() 的值。
强烈注意:
getMeasuredWidth() 是实际测量的宽度(真实)
getWidth() 是实际显示的宽度
上面的第 2 点,getWidth() 方法虽然在 onMeasure() 执行完就有值,但是现在的 getWidth() 只是临时值(和 getMeasuredWidth() 相等的临时值)。要经过 onLayout 方法的影响,getWidth() 才是真实显示的值,这个时,可能已经和 getMeasuredWidth() 不一样了。这就是这两个宽度的本质区别。
执行顺序
ViewGroup 常用的生命周期回调:构造方法、onFinishInflate、onMeasure、onSizeChanged、onLayout、onDraw、dispatchDraw。后两个是为 ViewGroup 绘制背景以及绘制子 View 的方法,这里不做过多讨论。首先,自定义类
public ViewTestLayout extends ViewGroup,实现前五个方法,并打印日志。如下:
public class ViewTestLayout extends FrameLayout{ // 索引标识 int index = 0; public ViewTestLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView("context attrs defStyle"); } public ViewTestLayout(Context context, AttributeSet attrs) { super(context, attrs); initView("context attrs"); } public ViewTestLayout(Context context) { super(context); initView("context"); } // 当构造方法被执行调用 private void initView(String from){ System.out.println("我来自:" + from + " = " + (++index) + "==" +getMeasuredWidth() + "==" + getWidth()); } // 当布局加载完成调用 @Override protected void onFinishInflate() { super.onFinishInflate(); System.out.println("onFinishInflate"+ " = " + (++index)+ "==" +getMeasuredWidth()+ "==" + getWidth()); } // 当尺寸改变调用 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); System.out.println("onSizeChanged"+ " = " + (++index)+ "==" +getMeasuredWidth()+ "==" + getWidth()); } // 当测量时调用 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); System.out.println("onMeasure"+ " = " + (++index)+ "==" +getMeasuredWidth()+ "==" + getWidth()); } // 当布局时调用 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { System.out.println("onLayout"+ " = " + (++index)+ "==" +getMeasuredWidth()+ "==" + getWidth()); } }
分别在这三种情况下输出日志:
在 activity_main.xml 中定义控件。
<com.example.customeview.ViewTestLayout android:id="@+id/vtl" android:layout_width="100dip" android:layout_height="100dip" android:layout_centerInParent="true" android:background="#6600ff00" > </com.example.customeview.ViewTestLayout>
在 MainActivity 中
setContentView( new ViewTestLayout(this) )
直接在 MainActivity
new ViewTestLayout(this)并不直接添加到任何父布局
可以得出的结论是
直接 new ,不添加到任何父布局,只会调用构造方法
onFinishInflate 只会在从 xml 中加载时调用,并且只调用一次。
初始化 ViewGroup 的流程大致为:构造方法创建对象->从布局加载(xml中定义时)->第一遍测量->开始改变尺寸->第一遍布局->第二遍测量->第二遍布局
从 xml 加载第一遍测量和第二遍测量,都会调用两次 onMeasure(值得注意)
getWidth() 和 getMeasuredWidth()
这两个都是获得自身的宽度和测量后的宽度,那么我们在自定义 ViewGroup 时,应该在什么时机使用,才能保证取到值。将上面的日志改为:
System.out.println("onFinishInflate"+ " = " + (++index)+ "==" +getMeasuredWidth()+ "==" + getWidth());
(1). 在 activity_main.xml 中定义控件。
(2). 在 MainActivity 中
setContentView( new ViewTestLayout(this) )
可以得出如下结论:
getMeasuredWidth(): 只要一执行完 setMeasuredDimension() 方法,就有值了,并且不再改变。
getWidth():必须执行完 onMeasure() 才有值,可能发生改变。
如果 onLayout 没有对子 View 实际显示的宽高进行修改,那么 getWidth() 的值 == getMeasuredWidth() 的值。
强烈注意:
getMeasuredWidth() 是实际测量的宽度(真实)
getWidth() 是实际显示的宽度
上面的第 2 点,getWidth() 方法虽然在 onMeasure() 执行完就有值,但是现在的 getWidth() 只是临时值(和 getMeasuredWidth() 相等的临时值)。要经过 onLayout 方法的影响,getWidth() 才是真实显示的值,这个时,可能已经和 getMeasuredWidth() 不一样了。这就是这两个宽度的本质区别。
总结
有了以上的实验,我们知道这些生命周的执行顺序,就可以知道在何时应该做何事。方法 | 建议 |
---|---|
构造方法 | 解析 attrs 的属性,初始化成员等 |
onFinishInflate | 初始化成员,通过 getChildAt 初始化布局中的子 View |
onSizeChanged | 获取自身或子 View 的实际宽高(建议在此时将宽高抽取成全局变量)(当调用 onMeasure 测量后,发现尺寸和原来的不一致就调用该方法) |
onMeasure | 测量,不必说 |
onLayout | 布局,不必说 |
生命周期全图
附上 Android View 的生命周期全图,作为参考方法 | 作用 |
---|---|
onFinishInflate() | 当View中所有的子控件均被映射成xml后触发 |
onMeasure( int , int ) | 确定所有子元素的大小 |
onLayout( boolean , int , int , int , int ) | 当View分配所有的子元素的大小和位置时触发 |
onSizeChanged( int , int , int , int ) | 当调用 onMeasure 测量后,发现尺寸和原来的不一致就调用该方法 |
onDraw(Canvas) | view渲染内容的细节 |
onKeyDown( int , KeyEvent) | 有按键按下后触发 |
onKeyUp( int , KeyEvent) | 有按键按下后弹起时触发 |
onTrackballEvent(MotionEvent) | 轨迹球事件 |
onTouchEvent(MotionEvent) | 触屏事件 |
onFocusChanged( boolean , int , Rect) | 当View获取或失去焦点时触发 |
onWindowFocusChanged( boolean ) | 当窗口包含的view获取或失去焦点时触发 |
onAttachedToWindow() | 当view被附着到一个窗口时触发 |
onDetachedFromWindow() | 当view离开附着的窗口时触发,该方法和 onAttachedToWindow() 是相反的 |
onWindowVisibilityChanged( int ) | 当窗口中包含的可见的view发生变化时触发 |
相关文章推荐
- zookeeper错误记录一;Cannot open channel to 2 at election address s1/192.168.253.131:3888 java.net.Connec
- [LeetCode]Count Primes
- Cocos2d-x笔记记忆整理Day2
- 实验二 单元测试
- 和我一起学批处理(2)
- Html5 CSS3新标签解释及用法
- 知识点
- hbase错误记录一; File /hbase/.tmp/hbase.version could only be replicated to 0 nodes instead of minReplica
- ubuntu 安装nodejs(很好用)
- Unity3D 学习笔记5 ——使用ScriptableObject进行序列化
- 日志管理方法和装置
- 你只是追逐时髦的码农
- Android开发有用的站点
- [JS][jQuery]remove()与 empty()的差别
- python 下的数据结构与算法---5:递归(Recursion)
- 互联网公司IT系统架构进化之路
- H264 MVD&MVP
- Android Event Listen
- 二叉树、树、森林的相互转换
- [LeetCode][二叉树]Balanced Binary Tree