关于view的一切
view的主要生命周期 [改变可见性] –> 构造View() –> onFinishInflate() –> onAttachedToWindow() –> onMeasure() –> onSizeChanged() –> onLayout() –> onDraw() –> onDetackedFromWindow()
1.View默认为可见的,不是默认值时先调用onVisibilityChanged(),但是此时该View的尺寸、位置等信息都不知道 2.android:visibility=gone时,onAttachedToWindow()执行后view的生命周期就结束了
view的事件分发机制
view的事件序列:手指按下屏幕到手指抬起为止
activity#dispatchTouchEvent(){
viewGroup#dispatchTouchEvent(){
if(onInterceptTouchEvent())onTouchExent();
for(View childView:childviews){
//view没有onInterceptTouchEvent()方法,所以一定会调用onTouch方法
//view的onTouchEvent方法默认消耗事件
if(childView#onTouch()||childView#onTouchExent()){
return;
}
}
//子类都没有处理,最终交由activity进行处理
handleEvent();
}
}
view滑动冲突的解决方法
1.外部拦截法:所有的事件都先经过父容器的拦截处理,如果父容器需要此类事件就拦截,否则就不拦截 通过重写父容器的onInterceptTouchEvent()方法来实现 代码实现
parent#onInterceptEvent(MotionEvent event){
boolean intercepted=false;
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
intercepted=false;
break;
case MotionEvent.ACTION_MOVE:
if(父类需要当前事件){
intercepted=true;
}else{
intercepted=false;
}
break;
case MotionEvent.ACTION_DOWN:
intercepted=false;
break;
}
}
2.内部拦截法:父类不处理任何事件,所有事件都传递给子元素,如果子元素需要此事件就直接消耗,否则就交由父容器处理 代码实现
childView#dispatchTouchEvent(MotionEvent event){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//设置父容器不再拦截任何事件
parent.requestDisallowInterceptTouchEvent(true);
case MotionEvent.ACTION_MOVE:
if(父类需要当前事件){
//父容器拦截该事件
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_DOWN:
break;
}
}
parent#onInterceptEvent(MotionEvent event){
boolean intercepted=false;
if(event.getAction()=MotionEvent.ACTION_DOWN){
return false;
}else{
return true;
}
}
view的工作原理
view的绘制流程
- ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw()。
- performMeasure()会调用最外层的ViewGroup的measure()–>onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild()这之中会用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起获取本View的MeasureSpec,然后调用子View的measure()到View的onMeasure()–>setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默认返回measureSpec的测量数值,所以继承View进行自定义的wrap_content需要重写。
- performLayout()会调用最外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个顶点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame()–>子View.layout()。
- performDraw()会调用最外层ViewGroup的draw():其中会先后调用background.draw()(绘制背景)、onDraw()(绘制自己)、dispatchDraw()(绘制子View)、onDrawScrollBars()(绘制装饰)。
- MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(对应精确值和match_parent)、AT_MOST(对应warp_content))和30位SpecSize组成一个int,DecorView的MeasureSpec由窗口大小和其LayoutParams决定,其他View由父View的MeasureSpec和本View的LayoutParams决定。ViewGroup中有getChildMeasureSpec()来获取子View的MeasureSpec。
可以获取view的宽高的地方(view已经初始化完毕)
- Activity/View#onWindowFocusChanged
- view.post(runable)
- ViewTreeObserver
- view.measuew(int,int)