代码改变世界

android touch event事件的传递顺序。

2014-04-03 19:37  东山冰雪  阅读(2214)  评论(0编辑  收藏  举报

最近在研究android的事件传递流程,在网上也发现了一些文章,但有的看起来不是很明白。先看看这几个事件的定义

public boolean dispatchTouchEvent (MotionEvent ev)

Since: API Level 1

Pass the touch screen motion event down to the target view, or this view if it is the target.

public boolean onInterceptHoverEvent (MotionEvent event)

Since: API Level 14

Implement this method to intercept hover events before they are handled by child views.

This method is called before dispatching a hover event to a child of the view group or to the view group's own onHoverEvent(MotionEvent) to allow the view group a chance to intercept the hover event. This method can also be used to watch all pointer motions that occur within the bounds of the view group even when the pointer is hovering over a child of the view group rather than over the view group itself.

 

public boolean onTouchEvent (MotionEvent event)

Since: API Level 1

Implement this method to handle touch screen motion events.

 

onInterceptTouchEvent:

onInterceptTouchEvent是在ViewGroup里面定义的。Android中的layout布局类一般都是继承此类的。onInterceptTouchEvent是用于拦截手势事件的,每个手势事件都会先调用onInterceptTouchEvent。

onTouchEvent:

onTouchEvent同样也是在view中定义的一个方法。处理传递到view 的手势事件。手势事件类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件

当一次点击事件发生的时候,首先是底层的activity收到dispatchTouchEvent ,然后传递给最底层的layout ,触发该layout的dispatchTouchEvent,然后调用该layout的onInterceptTouchEvent,然后传递给该布局上的控件,并触发控件的dispatchTouchEvent,然后触发onTouchEvent。

 

TouchDemoActivity
package com.example.touchdemo;


import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;

public class TouchDemoActivity extends Activity {
    public static final String TAG = "TouchDemoActivity";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.d("TouchDemoActivity", "onTouchEvent getAction:"+event.getAction());
        return super.onTouchEvent(event);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("TouchDemoActivity", "dispatchTouchEvent getAction:"+ev.getAction());
        return super.dispatchTouchEvent(ev);
    }
    @Override
    public boolean dispatchTrackballEvent(MotionEvent ev) {
        Log.d("TouchDemoActivity", "dispatchTrackballEvent getAction:"+ev.getAction());
        return super.dispatchTrackballEvent(ev);
    }
    
    
}
MyView
package com.example.touchdemo;


import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;

public class MyView extends Button {
    public static final String TAG = "MyView";
    public MyView(Context context){
        super(context);
    }
    
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent. getAction:"+event.getAction());
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(TAG, "dispatchTouchEvent. getAction:"+event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean dispatchTrackballEvent(MotionEvent event) {
        Log.e(TAG, "dispatchTrackballEvent. getAction:"+event.getAction());
        return super.dispatchTrackballEvent(event);
    }
    
    

}
<?xml version="1.0" encoding="utf-8"?>
<com.example.touchdemo.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
     >

    <com.example.touchdemo.MyView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</com.example.touchdemo.MyLayout>
布局文件

 

 

运行结果: 

04-03 19:07:30.805: D/TouchDemoActivity(8860): dispatchTouchEvent getAction:0
04-03 19:07:30.805: E/MyLayout(8860): dispatchTouchEvent. getAction:0
04-03 19:07:30.805: E/MyLayout(8860): onInterceptTouchEvent getAction:0
04-03 19:07:30.805: E/MyView(8860): dispatchTouchEvent. getAction:0
04-03 19:07:30.806: E/MyView(8860): onTouchEvent. getAction:0

这个是没有任何拦截的时候,事件是从底层逐步派发过去的,响应事件也是从上层逐步响应回来。

试着修改一下代码看看,我们先把MyView的onTouchEvent给返回false,说明该控件没有处理该事件,看看是什么效果。

04-03 19:46:30.520 D/TouchDemoActivity(12404): dispatchTouchEvent getAction:0
04-03 19:46:30.520 E/MyLayout(12404): dispatchTouchEvent. getAction:0
04-03 19:46:30.521 E/MyLayout(12404): onInterceptTouchEvent getAction:0
04-03 19:46:30.521 E/MyView  (12404): dispatchTouchEvent. getAction:0
04-03 19:46:30.521 E/MyView  (12404): onTouchEvent. getAction:0
04-03 19:46:30.521 E/MyLayout(12404): onTouchEvent getAction:0

从上面的运行效果可以看出,onTouchEvent回到了其上一级目录的onTouchEvent里面,也就是说,如果最上层的view不处理onTouchEvent,则会返回到上一级的onTouchEvent。

下面来看看viewgroup的onInterceptTouchEvent事件,如果该事件给返回了true,看有什么效果。

04-03 19:50:24.505 D/TouchDemoActivity(12940): dispatchTouchEvent getAction:0
04-03 19:50:24.505 E/MyLayout(12940): dispatchTouchEvent. getAction:0
04-03 19:50:24.506 E/MyLayout(12940): onInterceptTouchEvent getAction:0
04-03 19:50:24.506 E/MyLayout(12940): onTouchEvent getAction:0

从上面可以看出,只要处理了onInterceptTouchEvent,也不会继续往下传输了,而会调用当前viewgroup的ontouchevnent.

所以当你点击的layout的空白处的时候,事件也就到了当前的layout为止了,不会继续往下执行,这个时候就会调用当前layout的ontouchevnent。

今天就遇到一个问题,添加的滑动事件,在ontouchevnent和onInterceptTouchEvent都添加了,结果有时候会触发两次滑动事件,开始很奇怪为啥还是概率性出现的,后来才知道,如果touch的不是空白区域,则当前layout的ontouchevnent不会被触发,所以没有问题,而touch的如果是空白区域,则就会两个都被调用,造成重复调用的问题。