1、事件分发机制
用户点击产生一个MotionEvent,系统把这个事件分发给具体的view消费
2、MotionEvent介绍
手势接触屏幕产生的事件
MotionEvent对象操作有:
1、MotionEvent.ACTION_DOWN 手势按下,是所有触发操作中最开始的动作
2、MotionEvent.ACTION_UP 手势向上,是所有触发操作最后的动作
3、MotionEvent.ACTION_MOVE 手势按下后没有收回
4、MotionEvent.ACTION_CANCEL 事件结束 理论上是ACTION_UP 后执行,但也有可能是非人为因素导致的.
3、事件分发的本质
将MotinEvent传递到某个具体的view处理的过程
4、事件分发的对象及分发顺序
Activity ViewGroup View
Activity: 控制生命周期、处理事件
统筹视图的现实和添加、通过其它回调方法与Window、View交互.
View:所有UI组件的基类
ViewGroup:一组view的组合,其本身也是view的子类,是android布局的所有父类.比view多了可包含子view和定义布局参数的功能.
5、事件分发的过程协助者
协助者:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
dispatchTouchEvent():用来进行事件的分发,当点击事件能够传递给当前view,该方法就会被调用.返回结果受到当前View的TouchEvent和下级view的dispatchTouchEvent方法影响,表示是否消费当前的事件
onTouchEvent:在dispatchTouchEvent方法内部调用,用来处理事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再接受到后续的事件队列。而且onTouchEvent返回值由clickable和longclickable共同决定
onInterceptTouchEvent():在dispatchTouchEvent()内部调用.用来表示是否拦截当前的事件.如果当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会被调用,返回结果表示是否拦截当前事件。
6、代码说明演示
ViewGroup 代表 -》新建类DemoLayout 继承LinearLayout
public class DemoLayout extends LinearLayout {
private static final String TAG = "DemoLayout";
public DemoLayout(Context context) {
super(context);
}
public DemoLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DemoLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public DemoLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 父容器的分发事件
Log.i(TAG, "----父容器的分发事件"+ev.getAction());
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//父容器的拦截事件
Log.i(TAG, "----父容器的拦截事件"+ev.getAction());
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//父容器的触摸事件
Log.i(TAG, "----父容器的触摸事件"+ev.getAction());
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onTouchEvent(ev);
}
}
View 代表-》 新建类View集成Button
public class View extends android.support.v7.widget.AppCompatButton {
private static final String TAG = "View";
public View(Context context) {
super(context);
}
public View(Context context, AttributeSet attrs) {
super(context, attrs);
}
public View(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "----子容器的分发事件"+ event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "----子容器的触摸事件"+event.getAction());
return super.onTouchEvent(event);
}
}
操作准备:在layout布局中创建DemoLayout布局,包含View控件
android:layout_width="500dp"
android:layout_height="500dp"
android:clickable="true"
android:background="#000000">
android:id="@+id/view_touch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="事件分发"/>
初次运行看下运行流程:
没有拦截时
父容器把事件交给了子容器处理 dispatchTouchEvent进行事件分发 onInterceptTouchEvent没有拦截 子容器执行dispatchTouchEvent事件分发,再执行onTouchEvent事件
拦截后 代码和流程
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//父容器的拦截事件
Log.i(TAG, "----父容器的拦截事件"+ev.getAction());
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
DemoLayout dispatchTouchEvent进行事件分发 onInterceptTouchEvent拦截 父容器执行onTouchEvent,(备注:父容器clickable默认值是false即代表onTouchEvent 返回false,后续操作系列Action_UP不会执行,如下图,所以在xml中要声明clickable="true").从上看出拦截事件onIntercepeTpuchEvent不会再执行,只会被消费一次
若子容器不消费onTouchEvent,则事件会传递给父容器,而且不会接受后续操作,父容器onInterceptTouchEvent也不会再执行.同样若子容器在xml声明clickable="false"也就代表着子容器的onTouchEvent返回false。(备注操作:事件系列操作指的是按下ACTION_DOWN、移动ACTION_MOVE、抬起ACTION_UP,所谓后续操作如图子容器的ACTION_UP就没有再执行了)
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "----子容器的触摸事件"+event.getAction());
return false;
}
若子容器的onTouchEvent返回false,onClick方法也是不能够执行的,因为回调方法onClick()是在onTouchEvent中执行的
总结