1、例子
首先我们接着上一篇的代码,在代码中添加一个自定义的LinearLayout:
- package com.example.zhy_event03;
-
- import android.content.Context;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.widget.LinearLayout;
-
- public class MyLinearLayout extends LinearLayout
- {
- private static final String TAG = MyLinearLayout.class.getSimpleName();
-
- public MyLinearLayout(Context context, AttributeSet attrs)
- {
- super(context, attrs);
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev)
- {
- int action = ev.getAction();
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
- Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.e(TAG, "dispatchTouchEvent ACTION_UP");
- break;
-
- default:
- break;
- }
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event)
- {
-
- int action = event.getAction();
-
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
- Log.e(TAG, "onTouchEvent ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.e(TAG, "onTouchEvent ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.e(TAG, "onTouchEvent ACTION_UP");
- break;
-
- default:
- break;
- }
-
- return super.onTouchEvent(event);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev)
- {
-
- int action = ev.getAction();
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
- Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.e(TAG, "onInterceptTouchEvent ACTION_UP");
- break;
-
- default:
- break;
- }
-
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
- {
- Log.e(TAG, "requestDisallowInterceptTouchEvent ");
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
-
- }
继承LinearLayout,然后复写了与事件分发机制有关的代码,添加上了日志的打印~
然后看我们的布局文件:
- <com.example.zhy_event03.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity" >
-
- <com.example.zhy_event03.MyButton
- android:id="@+id/id_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="click me" />
-
- </com.example.zhy_event03.MyLinearLayout>
MyLinearLayout中包含一个MyButton,MyButton都上篇博客中已经出现过,这里就不再贴代码了,不清楚可以去查看~
然后MainActivity就是直接加载布局,没有任何代码~~~
直接运行我们的代码,然后点击我们的Button,依然是有意的MOVE一下,不然不会触发MOVE事件,看一下日志的输出:
- 09-06 09:57:27.287: E/MyLinearLayout(959): dispatchTouchEvent ACTION_DOWN
- 09-06 09:57:27.287: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_DOWN
- 09-06 09:57:27.287: E/MyButton(959): dispatchTouchEvent ACTION_DOWN
- 09-06 09:57:27.297: E/MyButton(959): onTouchEvent ACTION_DOWN
- 09-06 09:57:27.297: E/MyButton(959): onTouchEvent ACTION_MOVE
- 09-06 09:57:27.327: E/MyLinearLayout(959): dispatchTouchEvent ACTION_MOVE
- 09-06 09:57:27.327: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_MOVE
- 09-06 09:57:27.337: E/MyButton(959): dispatchTouchEvent ACTION_MOVE
- 09-06 09:57:27.337: E/MyButton(959): onTouchEvent ACTION_MOVE
- 09-06 09:57:27.457: E/MyLinearLayout(959): dispatchTouchEvent ACTION_UP
- 09-06 09:57:27.457: E/MyLinearLayout(959): onInterceptTouchEvent ACTION_UP
- 09-06 09:57:27.457: E/MyButton(959): dispatchTouchEvent ACTION_UP
- 09-06 09:57:27.457: E/MyButton(959): onTouchEvent ACTION_UP
可以看到大体的事件流程为:
MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent
2,方法的介绍和执行的顺序
dispatchTouchEcent:
只要事件传递到了当前View,那么dispatchTouchEcent方法就一定会被调用。返回结果表示是否消耗当前事件。
onInterceptTouchEvent:
在dispatchTouchEcent方法内部调用此方法,用来判断是否拦截某个事件。如果当前View拦截了某个事件,那么在这同一个事件序列中,此方法不会再次被调用。返回结果表示是否拦截当前事件。
onTouchEvent:
在dispatchTouchEcent方法内调用此方法,用来处理事件。返回结果表示是否处理当前事件,如果不处理,那么在同一个事件序列里面,当前View无法再收到后续的事件。
2,事件分发的顺序;
当点击事件传递到根ViewGroup里,会执行dispatchTouchEvent,
重点:在其内部会先调用onInterceptTouchEvent询问是否拦截事件,若拦截,则执行onTouchEvent方法处理这个事件;
若不拦截,则执行子元素的dispatchTouchEvent,onTouchEvent进入向下分发的传递,直到事件被处理。
3、关于拦截
1、如何拦截
上面的总结都是基于:如果没有拦截;那么如何拦截呢?
复写ViewGroup的onInterceptTouchEvent方法:
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev)
- {
- int action = ev.getAction();
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
-
- return true ;
- case MotionEvent.ACTION_MOVE:
-
- return true ;
- case MotionEvent.ACTION_UP:
-
- return true ;
- }
-
- return false;
- }
默认是不拦截的,即返回false;如果你需要拦截,只要return true就行了,这要该事件就不会往子View传递了,并且如果你在DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件。
原因很简单,当onInterceptTouchEvent(ev) return true的时候,会把mMotionTarget 置为null ;
2、如何不被拦截
如果ViewGroup的onInterceptTouchEvent(ev) 当ACTION_MOVE时return true ,即拦截了子View的MOVE以及UP事件;
此时子View希望依然能够响应MOVE和UP时该咋办呢?
Android给我们提供了一个方法:requestDisallowInterceptTouchEvent(boolean) 用于设置是否允许拦截,我们在子View的dispatchTouchEvent中直接这么写:
- @Override
- public boolean dispatchTouchEvent(MotionEvent event)
- {
- getParent().requestDisallowInterceptTouchEvent(true);
- int action = event.getAction();
-
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
- Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.e(TAG, "dispatchTouchEvent ACTION_UP");
- break;
-
- default:
- break;
- }
- return super.dispatchTouchEvent(event);
- }
重点: getParent().requestDisallowInterceptTouchEvent(true); 这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。
4、案例说明
(1)内外滑动方向不一致
看代码看不出所以然,我们通过实例来看看滑动冲突是怎么样的。我们先模拟第一种场景,内外滑动方向不一致,我们先自定义一个父控件,让其可以左右滑动,类似于ViewPaper:
然后将里面换成三个ListView:
可以看到左右滑动失效了,说明确实冲突了。那么我们就来解决一下,首先我们要明白滑动规则是什么,这个例子中如果我们竖直滑动就让ListView消耗事件,水平滑动就让我们自定义的父容器滑动。
知道了这个我们只需要将其替换到之前伪代码里的拦截条件里即可。
先用外部拦截法:
这里我们判断横向滑动的距离与竖直滑动距离的长短。若是竖直滑动的长,则判断为竖直滑动,那么就是ListView的滑动,就将intercepted置为false,让父容器不拦截,交由子元素ListView处理。若是横向,则intercepted置为true,交由父容器处理。
效果如下:
接下来看看内部拦截法:
先自定义一个MyListView继承ListView,重写其dispatchTouchEvent方法:
再重写外部父容器的oninterceptTouchEvent方法:
效果和外部拦截法一样。
好了,这篇文章到此结束,希望各位看了能对事件分发机制有个大致的了解,并且遇到了滑动冲突的问题能够迎刃而解。