关于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方法
		    //viewonTouchEvent方法默认消耗事件
			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的绘制流程

  1. ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw()。
  2. 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需要重写。
  3. performLayout()会调用最外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个顶点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame()–>子View.layout()。
  4. performDraw()会调用最外层ViewGroup的draw():其中会先后调用background.draw()(绘制背景)、onDraw()(绘制自己)、dispatchDraw()(绘制子View)、onDrawScrollBars()(绘制装饰)。
  5. 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已经初始化完毕)

  1. Activity/View#onWindowFocusChanged
  2. view.post(runable)
  3. ViewTreeObserver
  4. view.measuew(int,int)