菜鸟进阶之Android Touch事件传递(二)
2015-02-11 16:32
337 查看
这是touch事件传递系列博客的第二篇,如果想了解touch和click的那些事,请浏览投产事件传递系列的第一篇。http://blog.csdn.net/bingospunky/article/details/43603397
理理思路,我发现touch传递这部分的内容很多,所以每篇博客介绍一个方面比较好。这篇博客主要介绍touch事件传递的现象,一个简单的demo,让大家可以看到touch一步一步传递的过程。下篇博客中在研究源码是怎么实现的。再后面的博客会试图改变这篇文章看到的touch的传递过程,比如viewpager里嵌套listview。
[java] view
plaincopyprint?
public class LayoutView1 extends {
private final String TAG = "qingtian";
public LayoutView1(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView1 Intercept DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView1 Intercept MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView1 Intercept UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView1 Intercept CANCEL");
break;
}
return false;
}
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView1 Touch DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView1 Touch MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView1 Touch UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView1 Touch CANCEL");
break;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "LayoutView1 dispatch " );
boolean r = super.dispatchTouchEvent(ev);
Log.d(TAG, "LayoutView1 dispatch 结果 "+r);
return r;
}
}
代码B:
[java] view
plaincopyprint?
public class LayoutView2 extends LinearLayout {
private final String TAG = "qingtian";
public LayoutView2(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView2 Intercept DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView2 Intercept MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView2 Intercept UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView2 Intercept CANCEL");
break;
}
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView2 Touch DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView2 Touch MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView2 Touch UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView2 Touch CANCEL");
break;
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "LayoutView2 dispatch ");
boolean r = super.dispatchTouchEvent(ev);
Log.d(TAG, "LayoutView2 dispatch 结果 "+r);
return r;
}
}
代码C:
[java] view
plaincopyprint?
public class MyTextView extends TextView {
private final String TAG = "qingtian";
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "MyTextView Touch DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "MyTextView Touch MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "MyTextView Touch UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "MyTextView Touch CANCEL");
break;
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "MyTextView dispatch " );
boolean r = super.dispatchTouchEvent(ev);
Log.d(TAG, "MyTextView dispatch 结果 "+r);
return r;
}
// public void onClick(View v) {
// Log.d(TAG, "onClick");
// }
//
// public boolean onLongClick(View v) {
// Log.d(TAG, "onLongClick");
// return false;
// }
}
布局文件D:
[html] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<com.example.testontouch.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.example.testontouch.LayoutView2
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<com.example.testontouch.MyTextView
android:layout_width="200dp"
android:layout_height="150dp"
android:background="#FFFFFF"
android:text="Hello World"
android:textColor="#0000FF"
android:textSize="40sp"
android:textStyle="bold" />
</com.example.testontouch.LayoutView2>
</com.example.testontouch.LayoutView1>
你可以改变这三个方法的返回值,观察touch都会传递到哪里。
PS:网上有很多类似的demo,基本没有改变dispatchTouchEvent的返回值的(虽然实际开发中基本不会改变这个返回值),强烈建议修改这个方法的返回值,尤其对于:如果你了解touch传递的U形,改变这个值会改变U形,不是改变形,而是使U形变长或变短。
1.down事件首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
2.这篇博客里只有viewgroup和view这两种组件,viewgroup又会存在嵌套的关系,所以你在理解事件传递的时候一定要用点递归的概念。
3.当一个touch事件到达一个viewgroup时,先到达dispatchTouchEvent这个方法,然后到onInterceptTouchEvent方法:
a、如果onInterceptTouchEvent返回true,那么执行该viewgroup的onTouchEvent。由该viewgroup处理touch,不再传递到child组件。viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。
b、如果onInterceptTouchEvent返回false,那么寻找对应位置上的child:
b1、如果child存在,执行该child的dispatchTouchEvent:
b1一、如果child的dispatchTouchEvent返回true,表示child消化了该事件,viewgroup不做处理,viewgroup的onInterceptTouchEvent的返回值为true。
b1二、如果child的dispatchTouchEvent返回false,那么再执行viewgroup的onTouchEvent,viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。
b2、如果child不存在,那么执行该viewgroup的onTouchEvent,viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。
4.当一个touch事件到达一个view时,先到达dispatchTouchEvent这个方法,然后执行view的onTouchEvent方法。
5.这里忽略掉来view的mOnTouchListener,因为这个地方不会影响到dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,我们可以理解为mOnTouchListener和onTouchEvent是一个方法就行,只是再执行的时候他们二选其一。
6.我们把down->move........move->up成为一个完整的touch事件。对于一个完整的touch事件,touch在被传递的时候看起来是有记录它的传递路径的功能。对于viewgroup和view,我们不去重写它的方法,touch记录传递路径的功能有以下规律:
6a、如果down事件没有被任何一个view消化而被抛到最外面,那么不会有move和up事件。
6b、down事件被哪个view(包含viewgroup,因为viewgroup继承自view)消化,以后的move和up事件都会被这个view消化。
6c、如果down事件被消化了,后续的move和up会被传递到同一个方法里,但是传递的路径可能会不一样,可能会少经历一些方法。
demo下载路径:http://download.csdn.net/detail/u011647962/8440275
博客都写来20多篇了,一个回复都木有,很受伤啊,各位道友看到了给个回复啊,作为对一个菜鸟的鼓励。顺便说声:大家,过年好。
理理思路,我发现touch传递这部分的内容很多,所以每篇博客介绍一个方面比较好。这篇博客主要介绍touch事件传递的现象,一个简单的demo,让大家可以看到touch一步一步传递的过程。下篇博客中在研究源码是怎么实现的。再后面的博客会试图改变这篇文章看到的touch的传递过程,比如viewpager里嵌套listview。
demo
代码A:[java] view
plaincopyprint?
public class LayoutView1 extends {
private final String TAG = "qingtian";
public LayoutView1(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView1 Intercept DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView1 Intercept MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView1 Intercept UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView1 Intercept CANCEL");
break;
}
return false;
}
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView1 Touch DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView1 Touch MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView1 Touch UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView1 Touch CANCEL");
break;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "LayoutView1 dispatch " );
boolean r = super.dispatchTouchEvent(ev);
Log.d(TAG, "LayoutView1 dispatch 结果 "+r);
return r;
}
}
代码B:
[java] view
plaincopyprint?
public class LayoutView2 extends LinearLayout {
private final String TAG = "qingtian";
public LayoutView2(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView2 Intercept DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView2 Intercept MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView2 Intercept UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView2 Intercept CANCEL");
break;
}
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "LayoutView2 Touch DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "LayoutView2 Touch MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "LayoutView2 Touch UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "LayoutView2 Touch CANCEL");
break;
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "LayoutView2 dispatch ");
boolean r = super.dispatchTouchEvent(ev);
Log.d(TAG, "LayoutView2 dispatch 结果 "+r);
return r;
}
}
代码C:
[java] view
plaincopyprint?
public class MyTextView extends TextView {
private final String TAG = "qingtian";
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "MyTextView Touch DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "MyTextView Touch MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "MyTextView Touch UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "MyTextView Touch CANCEL");
break;
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "MyTextView dispatch " );
boolean r = super.dispatchTouchEvent(ev);
Log.d(TAG, "MyTextView dispatch 结果 "+r);
return r;
}
// public void onClick(View v) {
// Log.d(TAG, "onClick");
// }
//
// public boolean onLongClick(View v) {
// Log.d(TAG, "onLongClick");
// return false;
// }
}
布局文件D:
[html] view
plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>
<com.example.testontouch.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.example.testontouch.LayoutView2
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<com.example.testontouch.MyTextView
android:layout_width="200dp"
android:layout_height="150dp"
android:background="#FFFFFF"
android:text="Hello World"
android:textColor="#0000FF"
android:textSize="40sp"
android:textStyle="bold" />
</com.example.testontouch.LayoutView2>
</com.example.testontouch.LayoutView1>
你可以改变这三个方法的返回值,观察touch都会传递到哪里。
PS:网上有很多类似的demo,基本没有改变dispatchTouchEvent的返回值的(虽然实际开发中基本不会改变这个返回值),强烈建议修改这个方法的返回值,尤其对于:如果你了解touch传递的U形,改变这个值会改变U形,不是改变形,而是使U形变长或变短。
总结
Android官方文档上
onInterceptTouchEvent()与onTouchEvent()的机制:1.down事件首先会传递到onInterceptTouchEvent()方法
2.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理
3.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理
5.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
我的理解
1.touch事件传递与dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent这三个方法相关,可以看到这三个方法都返回boolean类型的值,如果我们能了解这三个方法的返回值的含义,就在自定义组件的时候通过控制返回的值影响touch事件的传递,这是理解的第一个层次。如果能从源码里真正理解来这三个返回值是怎么被使用的,那么我们在自定义view的时候可以重写使用返回值的部分,那么我们就可以改变touch传递的U形,更深的理解touch,比如我一直想弄明白的一个问题:viewpager里嵌套listview,怎么实现区分viewpage响应滑动,还是listview响应滑动,这是我理解的更高的一个层次。当然第二层我还没有达到,写完这个系列的博客后应该可以达到。2.这篇博客里只有viewgroup和view这两种组件,viewgroup又会存在嵌套的关系,所以你在理解事件传递的时候一定要用点递归的概念。
3.当一个touch事件到达一个viewgroup时,先到达dispatchTouchEvent这个方法,然后到onInterceptTouchEvent方法:
a、如果onInterceptTouchEvent返回true,那么执行该viewgroup的onTouchEvent。由该viewgroup处理touch,不再传递到child组件。viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。
b、如果onInterceptTouchEvent返回false,那么寻找对应位置上的child:
b1、如果child存在,执行该child的dispatchTouchEvent:
b1一、如果child的dispatchTouchEvent返回true,表示child消化了该事件,viewgroup不做处理,viewgroup的onInterceptTouchEvent的返回值为true。
b1二、如果child的dispatchTouchEvent返回false,那么再执行viewgroup的onTouchEvent,viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。
b2、如果child不存在,那么执行该viewgroup的onTouchEvent,viewgroup的onInterceptTouchEvent的返回值根据该viewgroup的onTouchEvent的返回值确定。
4.当一个touch事件到达一个view时,先到达dispatchTouchEvent这个方法,然后执行view的onTouchEvent方法。
5.这里忽略掉来view的mOnTouchListener,因为这个地方不会影响到dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,我们可以理解为mOnTouchListener和onTouchEvent是一个方法就行,只是再执行的时候他们二选其一。
6.我们把down->move........move->up成为一个完整的touch事件。对于一个完整的touch事件,touch在被传递的时候看起来是有记录它的传递路径的功能。对于viewgroup和view,我们不去重写它的方法,touch记录传递路径的功能有以下规律:
6a、如果down事件没有被任何一个view消化而被抛到最外面,那么不会有move和up事件。
6b、down事件被哪个view(包含viewgroup,因为viewgroup继承自view)消化,以后的move和up事件都会被这个view消化。
6c、如果down事件被消化了,后续的move和up会被传递到同一个方法里,但是传递的路径可能会不一样,可能会少经历一些方法。
demo下载路径:http://download.csdn.net/detail/u011647962/8440275
写在最后
博客都写来20多篇了,一个回复都木有,很受伤啊,各位道友看到了给个回复啊,作为对一个菜鸟的鼓励。顺便说声:大家,过年好。
相关文章推荐
- 菜鸟进阶Android Touch事件传递(四)
- 菜鸟进阶之Android Touch事件传递(一)
- 菜鸟进阶之Android Touch事件传递(四)
- 菜鸟进阶之Android Touch事件传递(三)
- 菜鸟学android——touch事件的传递顺序
- Android Touch事件传递机制解析
- Android TouchEvent事件传递机制
- Android中view的Touch事件传递顺序
- Android事件传递机制【Touch事件】
- Android Touch事件传递机制解析
- Android事件传递机制【Touch事件】
- Android事件传递机制【Touch事件】
- Android事件传递机制【Touch事件】
- android事件传递机制以及onInterceptTouchEvent()和onTouchEvent()详解
- android事件分析(二)——MOTIONEVENT事件在ONINTERCEPTTOUCHEVENT()、ONTOUCHEVENT()中的传递顺序
- Android Touch事件传递机制解析 android:clickable
- Android事件传递机制【Touch事件】
- Android事件传递机制【Touch事件】
- Android中ViewGroup到View的Touch事件的传递机制
- 详细剖析 android onInterceptTouchEvent(MotionEvent event) 和 onTouchEvent(MotionEvent event) 的事件传递机制