Android中View绘制流程
来源:互联网 发布:python android接口 编辑:程序博客网 时间:2024/06/05 04:04
View的工作流程主要分为onMeasure、onSizeChanged、onLayout、onDraw;
- onMeasure
- MeasureSpec
- onSizeChangedint w int h int oldw int oldh
- onLayout
- onDraw
- invalidate
- 自定义View注意事项
- onMeasure
onMeasure
onMeasure方法在控件的父元素正要放置它的子控件时调用。它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec。它们指明控件可获得的空间以及关于这个空间描述的元数据。具体使用如下所示:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); int width = 0; int height = 0; // 记录每一行的宽度与高度 int lineWidth = 0; int lineHeight = 0; // 得到内部元素的个数 int cCount = getChildCount(); for (int i = 0; i < cCount; i++) { View child = getChildAt(i); // 测量子View的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); // 得到LayoutParams MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); // 子View占据的宽度 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; child.setLayoutParams(lp); // 子View占据的高度 int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; // 换行 if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) { // 对比得到最大的宽度 width = Math.max(width, lineWidth); // 重置lineWidth lineWidth = childWidth; // 记录行高 height += lineHeight; lineHeight = childHeight; } else // 未换行 { // 叠加行宽 lineWidth += childWidth; // 得到当前行最大的高度 lineHeight = Math.max(lineHeight, childHeight); } // 最后一个控件 if (i == cCount - 1) { width = Math.max(lineWidth, width); height += lineHeight; } } setMeasuredDimension( // modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(), modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom()// ); }
MeasureSpec
MeasureSpec代表一个32位int值,高两位代表SpecMode,低30位代表SpecSize,封装了父容器对 view 的布局上的限制,内部提供了宽高的信息( SpecMode 、 SpecSize ),SpecSize是指在某种SpecMode下的参考尺寸。
SpecMode值有如下三种:
- UNSPECIFIED:父容器不对 view 有任何限制,要多大给多大
- EXACTLY:父容器已经检测出 view 所需要的大小
- AT_MOST:父容器指定了一个大小, view 的大小不能大于这个值
关于应用层View,这里是指我们布局中的view,其MeasureSpec的创建遵循下表中的规则
针对上表,这里再做一下具体的说明。前面已经提到,对于应用层 View ,其 MeasureSpec 由父容器的 MeasureSpec 和自
身的 LayoutParams 来共同决定,那么针对不同的父容器和view本身不同的LayoutParams,view就可以有多种MeasureSpec。这里简单说下,当view采用固定宽高的时候,不管父容器的MeasureSpec是什么,view的MeasureSpec都是精确模式并且其大小遵循Layoutparams中的大小;当view的宽高是match_parent时,这个时候如果父容器的模式是精准模式,那么view也是精准模式并且其大小是父容器的剩余空间,如果父容器是最大模式,那么view也是最大模式并且其大小不会超过父容器的剩余空间;当view的宽高是wrap_content时,不管父容器的模式是精准还是最大化,view的模式总是最大化并且大小不能超过父容器的剩余空间。可能大家会发现,在我们的分析中漏掉了Unspecified模式,这个模式主要用于系统内部多次measure的情况下,一般来说,我们不需要关注此模式。
onSizeChanged(int w, int h, int oldw, int oldh)
这个是系统回调方法,在这个View的大小发生改变的时候会被系统调用,我们要监控view的大小变化,重写这个方法就可以了。
onLayout
主要作用:为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。当 viewgroup 的位置被确定后,它在 onLayout 会遍历所有的 child 并调用其 layout 。在 layout 中 onLayout 会被调用。
/**
* 存储所有的View
*/
private List
onDraw
绘图时在onDraw中绘制,如果是GONE状态下,View的绘制周期会在onLayout的时候停止,不会执行这个方法。
draw 的大致流程
a. 画背景 background.draw(canvas)
b. 绘制自己( onDraw )
c. 绘制 children(dispatchDraw )
d. 绘制装饰( onDrawScrollBars )
备注:dispatchDraw 会遍历调用所有 child 的 draw ,如此 draw 事件就一层层地传递了下去
invalidate
用于刷新View
说明:请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”视图,即谁(View的话,只绘制该View;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。
一般引起invalidate()操作的函数如下:
- 直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。
- setSelection()方法:请求重新draw(),但只会绘制调用者本身,listview.setselection(position),表示将列表移动到指定的Position处。
- setVisibility()方法:当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。
- setEnabled()方法:请求重新draw(),但不会重新绘制任何视图包括该调用者本身。
自定义View注意事项
让MyView支持Warp_content 如果不在onMeasure()方法中对warp_content做特殊处理,就不能达预期的效果。
解决方法是对在onMeasure()方法中做相应的处理,把需要设定的宽或者高,设置成匹配内容宽高的int值,当然也要考虑内边距padding。
思路如下:判断widthMeasureSpec和heightMeasureSpec是否为AT_MOST,也就是wrap_content,如果想让实际宽度为占满父控件,就把widthSpecSize当做参数,如果不想这样,想要自己定义宽度,就填入具体数值,也可以计算内容的宽度来作为参数。让MyView支持padding 这是因为直接继承了View的控件,如果不在draw方法中处理padding,那么padding属性是无法起作用的。另外,如果直接继承自ViewGroup的控件,需要在onMeasure()和onLayout()中考虑padding和子元素的margin对其造成的影响,不然将导致padding和子元素的margin失效。(padding是内边距,需要控件自己控制,而margin是外边距,由父控件影响)
- 尽量不要在View中使用Handler,因为View本身就提供了post系列的方法,完全可以替代Handler的作用。
- View中如有线程或者动画,需要及时停止: 如果有线程或者动画需要停止时,那么onDetachedFromWindow是一个很好的时机。当包含此View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用,和此方法对应的是onAttachedToWindow方法会被调用。当View变得不可见时也需要停止线程和动画,如果不及时处理这种问题,有可能会造成内存泄漏。
- 注意特殊情况下的View滑动冲突
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android中View绘制流程
- Android学习 - Android中View绘制流程
- Android中View绘制流程以及invalidate()
- Android中View的绘制流程
- Android中View绘制流程以及invalidate()
- Android中View的绘制流程
- Android中View的绘制流程详解
- Android中View的绘制流程详解
- android中View的绘制流程
- volleyjar包,源码导入与关联
- Mysql主从复制
- mysql存储引擎(一)
- 线程的问题
- css盒子模型的示例
- Android中View绘制流程
- 二叉查找树中搜索区间
- mysql connector c++测试
- .NET程序集强命名删除与再签名技术 源代码剖析
- UIButton文字居左显示
- ZOJ 3878-Convert QWERTY to Dvorak【模拟】
- KMeans算法检测网络异常入侵
- 极限学习机(ELM)的使用
- The 3n + 1 problem