view和viewGroup事件区别
这里主要说下在事件分发上的区别,绘制上面也是有区别的,我们都看过相关源码。
这里总结一下:
①事件分发
事件分发机制主要有三个方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
1.ViewGroup包含这三个方法,而View则只包含dispatchTouchEvent()、onTouchEvent()两个方法,不包含onInterceptTouchEvent()。
2.触摸事件由Action_Down、Action_Move、Action_Up组成,一次完整的触摸事件,包含一个Down和Up,以及若干个Move(可以为0);
3.在Action_Down的情况下,事件会先传递到最顶层的ViewGroup,调用ViewGroup的dispatchTouchEvent(),①如果ViewGroup的onInterceptTouchEvent()返回false不拦截该事件,则会分发给子View,调用子View的dispatchTouchEvent(),如果子View的dispatchTouchEvent()返回true,则调用View的onTouchEvent()消费事件。②如果ViewGroup的onInterceptTouchEvent()返回true拦截该事件,则调用ViewGroup的onTouchEvent()消费事件,接下来的Move和Up事件将由该ViewGroup直接进行处理。
4.当某个子View的dispatchTouchEvent()返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下来的Move和Up事件将由该子View直接进行处理。
5.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch();触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。
6..由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上层ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象。如ViewGroup0——ViewGroup1——TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,将被保存在ViewGroup0中;当Move和Up事件来时,会先从ViewGroup0传递到ViewGroup1,再由ViewGroup1传递到TextView,最后事件由TextView消费掉。
7.子View可以调getParent().requestDisallowInterceptTouchEvent(),请求父ViewGroup不拦截事件
②绘制原理
UI绘制主要有五个方法:onDraw(),onLayout(),onMeasure(),dispatchDraw(),drawChild()
1.ViewGroup包含这五个方法,而View只包含onDraw(),onLayout(),onMeasure()三个方法,不包含dispatchDraw(),drawChild()。
2.绘制流程:onMeasure(测量)——>onLayout(布局)——>onDraw(绘制)。
3.onLayout():对于View来说,onLayout()只是一个空实现;而对于ViewGroup来说,onLayout()使用了关键字abstract的修饰,要求其子类必须重载该方法,目的就是安排其children在父视图的具体位置。
4.draw过程:drawBackground()绘制背景——>onDraw()对View的内容进行绘制——>dispatchDraw()对当前View的所有子View进行绘制——>onDrawScrollBars()对View的滚动条进行绘制。
viewpager和listview的事件冲突
滑动冲突的解决方案:
1.外部拦截法
具体做法就是当你不想把事件传递给子控件的时候在onInterceptTouchEvent方法中返回true即可拦截事件,这时候子控件将不会再接收到这一次的touch事件流(所谓touch事件流是以ACTION_DOWN开始,中间包含若干个ACTION_MOVE,以ACTION_UP结束的一连串事件)。伪代码如下:
public boolean onInterceptTouchEvent(MotionEvent ev) {
if ( condition ) {
return true;
}
return false;
}
condition就得根据具体情形来设定。
2.内部拦截法
首先,我们让父控件拦截除了ACTION_DOWN以外的所有事件,如果连ACTION_DOWN都拦截那么子控件将无法收到任何touch事件:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
然后,在控件的内部分发事件的时候请求需要的事件(实际上就是禁止父控件拦截事件):
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
//通知父容器不要拦截事件
parent.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if ( <condition> ){
//通知父容器拦截此事件
parent.requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
parent.requestDisallowInterceptTouchEvent(false);
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
这样,就可以解决touch事件的冲突问题,从控件本身解决。内部拦截法使用起来稍显复杂,需要修改两个控件,一般情况下我们都通过外部拦截法解决滑动冲突,如果有特殊情况需要使用内部拦截法才会使用内部拦截法。
写在最后
关于这两种解决方案的实例以后会补上,再具体说明。