一、事件分发机制java
用户点击产生一个MotionEvent,系统把这个事件分发给具体的view消费android
二、MotionEvent介绍json
手势接触屏幕产生的事件 app
MotionEvent对象操做有:ide
一、MotionEvent.ACTION_DOWN 手势按下,是全部触发操做中最开始的动做布局
二、MotionEvent.ACTION_UP 手势向上,是全部触发操做最后的动做spa
三、MotionEvent.ACTION_MOVE 手势按下后没有收回code
四、MotionEvent.ACTION_CANCEL 事件结束 理论上是ACTION_UP 后执行,但也有多是非人为因素致使的.xml
三、事件分发的本质对象
将MotinEvent传递到某个具体的view处理的过程
四、事件分发的对象及分发顺序
Activity ViewGroup View
Activity: 控制生命周期、处理事件
统筹视图的现实和添加、经过其它回调方法与Window、View交互.
View:全部UI组件的基类
ViewGroup:一组view的组合,其自己也是view的子类,是android布局的全部父类.比view多了可包含子view和定义布局参数的功能.
五、事件分发的过程协助者
协助者:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()
dispatchTouchEvent():用来进行事件的分发,当点击事件可以传递给当前view,该方法就会被调用.返回结果受到当前View的TouchEvent和下级view的dispatchTouchEvent方法影响,表示是否消费当前的事件
onTouchEvent:在dispatchTouchEvent方法内部调用,用来处理事件,返回结果表示是否消耗当前事件,若是不消耗,则在同一个事件序列中,当前view没法再接受到后续的事件队列。并且onTouchEvent返回值由clickable和longclickable共同决定
onInterceptTouchEvent():在dispatchTouchEvent()内部调用.用来表示是否拦截当前的事件.若是当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会被调用,返回结果表示是否拦截当前事件。
六、代码说明演示
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控件
<com.example.sunyuanfei.myapplication.DemoLayout android:layout_width="500dp" android:layout_height="500dp" android:clickable="true" android:background="#000000"> <com.example.sunyuanfei.myapplication.View android:id="@+id/view_touch" android:layout_width="match_parent" android:layout_height="match_parent" android:text="事件分发"/> </com.example.sunyuanfei.myapplication.DemoLayout>
初次运行看下运行流程:
没有拦截时
父容器把事件交给了子容器处理 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中执行的
总结