View的常用回调:onAttachedToWindow(),onVisibilityChanged(),onDetachedFromWindow()
和window相关的两个方法可以用来做变量的初始化和回收,比如监听事件的添加和释放
中间的可见性变化,个人用到是因为定制的framework有缺陷,规避缺陷。
自定义view分为两种:
- 继承现有ViewGroup,实现组合控件,按需添加自定义属性、业务等。
- 继承View(Group),略加修改,添加业务或者重写onMeasure等方法
界面流程:
activity创建之后,会添加DectorView到Window中,同时会创建ViewRoot(以ViewRootImpl对象的形式),并将两者关联起来。
setContentView()是因为DecorView作为顶级View(FrameLayout),一般包含一个LinearLayout,分为标题和内容,而内容就是content,对应布局id是android.R.id.content
而View的绘制,则是通过ViewRoot执行performTraversals开始。包括performMeasure-->measure-->onMeasure-->performLayout-->layout-->onLayout-->performDraw-->draw-->onDraw
View的测量,通过MeasureSpec转化,而MeasureSpec的确定,则通过外层限制和自身属性确定。比如DecorView由窗口尺寸和自身LayoutParams确定;常规View由父容器的MeasureSpec和LayoutParams确定。
onMeasure:
View里面调用getDefaultSize计算测量值,该方法表明:Unspecified的情况下,取最小宽度、背景最小宽度(原始宽度)的大值,否则取parentSpecSize。再把测量值赋值给变量mMeasuredWidth,...;
可以看到AT_MOST和EXACTLY取的值都是parentSpecSize。所以自己继承view的时候,需要在onMeasure中处理wrap_content的情况,给与默认的宽/高,而具体值没有给定依据。
而ViewGroup则是结合模式和子View的宽高进行设置。可以自己翻看
onMeasure之后,可以getMeasuredWidth和getMeasuredHeight
因为activity生命周期和View的测量不是同步的,所以在onResume之前不能保证View已经执行完onMeasure。可以用这几个方法解决:activity/view#onWindowFocusChanged,view#post,ViewTreeObserver,view.measure(match_parent的时候不能做到,具体数值的时候传入EXACTLY和尺寸即可,wrap的时候传入AT_MOST和(1<<30)-1)
onLayout:
setFrame和layout递归调用
onLayout之后,可以getWidth和getHeight
getMeasuredWidth和getWidth赋值的时间不同,值在默认情况(即正常的layout)下相同。
onDraw:
绘制背景background.draw,绘制自己onDraw,绘制子视图dispatchDraw,绘制装饰onDrawScrollBars
setWillNotDraw。在有些ViewGroup中被置为true,自己处理的时候注意
MeasureSpec:
View的静态内部类,将模式和大小值存储在一个int值中,前2位代表模式SpecMode,后30位代表大小SpecSize。
模式 | 对应LayoutParams | 说明 |
---|---|---|
UNSPECIFIED | / | 父类未对子类约束,可以为任何值 |
EXACTLY | match_parent和具体的值 | 已检测出大小,即SpecSize的值 |
AT_MOST | wrap_content | 已指定最大值SpecSize |
遗留问题:
1、View的makeMeasureSpec()中,sUseBrokenMakeMeasureSpec会在<=17的时候置为true,理由是RelativeLayout的兼容性:
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
那么问题就是,为什么会有兼容性问题,因为size可能为负?
2、为什么getDefaultSize中AT_MOST和EXACTLY返回大小都是parentSpecSize?感觉完全可以做到父类中区分?
摘自:
开发艺术探索,第4章,view的工作原理
网友评论