您的位置:首页 > 移动开发 > Android开发

菜鸟进阶之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。

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多篇了,一个回复都木有,很受伤啊,各位道友看到了给个回复啊,作为对一个菜鸟的鼓励。顺便说声:大家,过年好。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: