(1)在自定义view的时候常常会遇到事件拦截处理,好比在侧滑菜单的时候,咱们但愿在侧滑菜单里面有listview控件,可是咱们但愿既能左右滑动又能上下滑动,这个时候就须要对触摸的touch事件进行拦截。这个时候咱们就须要明白android touch 事件传递机制,android
(2)之前不少时候比较模糊,也许是网上看到也有不少事件传递的相关文章,但我看着头晕,解释不完全,有的说得一半,总算不知足不满意,因而据我本身的理解来完全的来整理下具体的是怎么个传递方式,以最简单通俗易懂的方式分享给你们,但愿你们看到有什么不对的地方及时提出纠正。谢谢 ide
这是本次理解android touch 事件传递机制的布局文件布局
(1)android touch 事件传递机制示意图,因为网页缘由会被拉伸,请你们单独将该图在另外一个窗口打开查看。测试
(2)事件是从Activity触发事件而后传递到布局文件,一层一层的往子容器传递到最底层的view,若是每层布局文件未对该事件进行处理或者消费那么该事件会从最底层开始往上传到Activity进行消费。相似于一个U型。spa
(3)那么事件的发起是由Activity界面的touch事件发起传递到布局视图,可是该视图只是描述了布局文件或者view的相关事件传递机制,Activity事件没有进行描述,可是在下面测试中会涉及Activity相关事件传递来解释心中些许疑惑日志
android touch 事件传递机制示意图能够总结以下规律xml
(0)忘掉之前的各类说法解释,由于各类组合说来讲去的,咱们须要简单的,换一个角度,换一个思惟,忘记事件传递,以最简单通俗易懂的方式理解事件传递,我只须要一张图就搞定事件传递机制。blog
(1)一个事件由用户点击触发开始顺着箭头的方向进行传递,直到任意一个结束点结束事件传递。继承
(2)那么事件传递能够由A传到B,B能够不传到C,B不进行分发,那么就从B再传回A进行消费而后结束,也能够由B传到C而后传递D,或者不传到D进行消费或者传到父容器进行消费结束。事件
(1)事件从Activity界面开始 事件处理方法有两个 dispatchTouchEventon 和 TouchEvent 两种
(2)继承ViewGroup的子类事件处理有dispatchTouchEvent、onInterceptTouchEvent 和 onTouchEvent三种
(3)继承View的子类事件处理有dispatchTouchEventon 和 TouchEvent 两种
(1)touch事件事件传递形象的理解能够这么认为:好比我有一个苹果(touch),我能够本身吃也能够分发(dispatchTouchEventon)给孩子吃(TouchEvent)。若是我不吃那么我就返回给个人父亲处理,若是我分发给孩子那么孩子,那么这个苹果交给个人孩子他有本身独立的权利进行处理,他能够继续分给他的孩子就是个人孙子进行处理,也能够本身吃了吃掉,若是个人孙子不处理他也能够返回给他的父亲就是个人孩子处理,个人孩子也有相同的权利进行处理。
(2)接着上面其实这个事件(苹果)的传递是从上往下,而后再由下往上传递,中途若是有人消费这个事件(吃掉苹果),那么这个事件就结束(苹果没有了),就结束传递。
(3)事件(苹果)传递,不像咱们人同样要害羞要矜持,推来推去,好比这个苹果孩子不分发给他的孩子可是他本身又不想消费(吃掉)而是返回给我,那么我就是只有两个选择要么消费(吃掉)要么返回给个人父类进行处理,不能推来推去,就是不能孩子给我了事件(苹果),我又来分发给孩子,这是不行的,这样就是个死循环。
(4)总之我拿到这个事件(苹果)会往个人孩子进行传递,个人孩子也能够往他的孩子进行传递和消费,这样转往下走,若是有一个孩子消费掉这个事件(吃掉苹果),那么该事件结束。若是孩子都不消费那么就会从最下面的孩子一层层传上来传到我手里进行处理。
(5)记住 分发 拦截 处理。任何孩子拿到该事件第一步就是往下面分发,若是中途有拦截那么久就本身处理,直到分到最底层就辈分最低的孩子,若是该事件就往上给父亲处理。
(1)事件不管是从开始触发仍是在传递到不一样的层级布局文件过程当中,必定会对事件进行分发,固然在父容器中若是touch事件被拦截了就不会下传了。就是若是该事件传递到某一层那么该层的dispatchTouchEventon会首先被调用进行事件分发,好比示意图中的事件传递到C-ViewGroup层,那么C-ViewGroup中的 dispatchTouchEventon这个方法是必定会被调用的进行事件分发,固然若是事件在B-ViewGroup不分发事件就会往上传递给父类的onTouchEvent进行处理,或者在B-ViewGroup被拦截发事件就会往上传递给B-ViewGroup的onTouchEvent进行处理,也就不会传到C-ViewGroup这层。
(2)在对事件进行分发中,dispatchTouchEventon返回值为false表示对事件进行分发,返回true为不分发,当事件不进行分发的时候,那么该事件在该层就不会往下传递,就会返回传递给父容器onTouchEvent进行处理,那么若是该事件被拦截事件就传递给当前容器onTouchEvent进行处理,若是当前容器onTouchEvent返回false就表示不想消费处理,那么该事件就会往上传递给父容器onTouchEvent进行处理,记住onTouchEvent这里不能往下传,若是这里不消费就只能往上传递,只有dispatchTouchEventon才能往下分发。
1、在测试验证以前须要了解每一个View的子类都具备下面三个和TouchEvent处理密切相关的方法:
(1)public boolean dispatchTouchEvent(MotionEvent ev) 分发TouchEvent
(2)public boolean onInterceptTouchEvent(MotionEvent ev) 拦截TouchEvent
(3)public boolean onTouchEvent(MotionEvent ev) 处理TouchEvent
<?xml version="1.0" encoding="utf-8"?>
<boyoi.com.event.transfer.ALinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#4bf3a8">
<boyoi.com.event.transfer.BViewGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
android:background="#4a8f8f">
<boyoi.com.event.transfer.CViewGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
android:background="#847834">
<boyoi.com.event.transfer.DView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
android:background="#765982">
</boyoi.com.event.transfer.DView>
</boyoi.com.event.transfer.CViewGroup>
</boyoi.com.event.transfer.BViewGroup>
</boyoi.com.event.transfer.ALinearLayout>
布局文件相关代码
/**
* Created by yishujun on 16/6/12.
*/
public class ALinearLayout extends LinearLayout{
private final String TAG = "ALinearLayout";
public ALinearLayout(Context context) {
super(context);
}
public ALinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ALinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG,"onInterceptTouchEvent" );
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
/**
* Created by yishujun on 16/6/12.
*/
public class BViewGroup extends ViewGroup{
private final String TAG = "BViewGroup";
public BViewGroup(Context context) {
super(context);
}
public BViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//该demo主要讲解android的事件传递,这里不解释,若有疑问请关注我接下来的自定义view相关博客
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//该demo主要讲解android的事件传递,这里不解释,,若有疑问请关注我接下来的自定义view相关博客
//下面为何这么处理,由于例子中只有一共孩子,getChildAt(0)只能是只有一个孩子,否则得不到咱们要的效果
View childView = getChildAt(0);
childView.layout(l, t, r-2*l, b-2*t);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG,"onInterceptTouchEvent" );
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
/**
* Created by yishujun on 16/6/12.
*/
public class CViewGroup extends ViewGroup {
private final String TAG = "CViewGroup";
public CViewGroup(Context context) {
super(context);
}
public CViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//该demo主要讲解android的事件传递,这里不解释,若有疑问请关注我接下来的自定义view相关博客
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//该demo主要讲解android的事件传递,这里不解释,,若有疑问请关注我接下来的自定义view相关博客
//下面为何这么处理,由于例子中只有一共孩子,getChildAt(0)只能是只有一个孩子,否则得不到咱们要的效果
View childView = getChildAt(0);
childView.layout(l, t, r-2*l, b-2*t);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG,"onInterceptTouchEvent" );
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
/**
* Created by yishujun on 16/6/12.
*/
public class DView extends View{
private final String TAG = "DView";
public DView(Context context) {
super(context);
}
public DView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 计算出本身的宽高
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
测试用例一:因此的 touch事件都分发不拦截不消费,全部的dispatchTouchEvent,onInterceptTouchEvent,onTouchEven方法t都返回false,如以上代码默认代码,点击中间的D-View:
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/CViewGroup: onInterceptTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/DView: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/DView: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/BViewGroup: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
06-12 11:09:56.377 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:09:56.377 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
测试用例二:在mainActivity界面对touch事件不进行分发,mainActivity 里面的dispatchTouchEvent返回true 则结果:
06-12 11:26:24.288 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:26:24.390 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
由以上日志可知,若是Activity界面不进行事件分发那么事件将没法往下传递
测试用例三:在mainActivity界面对touch事件进行分发,可是在mainActivity 对该事件进行消费,不往下面传递,mainActivity 里面的 dispatchTouchEvent返回false,onTouchEvent 返回true 则结果:
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/CViewGroup: onInterceptTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/DView: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/DView: onTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/BViewGroup: onTouchEvent
06-12 11:32:06.522 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onTouchEvent
06-12 11:32:06.522 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
06-12 11:32:06.634 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:32:06.634 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
结果与测试用例同样的效果
测试用例四:在CViewGroup界面对touch事件进行拦截, 并本身消费该事件,CViewGroup 里面的 onInterceptTouchEvent返回true,onTouchEvent 返回true 则结果:
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
由以上日志能够看到,事件传递到CViewGroup就不在往下传递了,也不往上传递了,本身消费了
测试用例五:在CViewGroup界面对touch事件进行拦截, 本身不消费该事件,CViewGroup 里面的 onInterceptTouchEvent返回true,onTouchEvent 返回false 则结果:
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/CViewGroup: onInterceptTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/BViewGroup: onTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
06-12 11:41:23.936 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:41:23.936 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
由以上日志能够看到,事件传递到CViewGroup就不在往下传递了,可是本身也并未消费,而是将该事件传递给父容器了
好了其余的测试例子本身能够进行尝试,我就不一一尝试了,若是有疑问或者错误须要纠正请留言,我通常看到会及时回答,谢谢交流沟通。后面接下来我还会写一些自定义控件来阐述事件传递在何时用和怎么用,但愿支持鼓励。