android事件分发机制

首先我们要明确三个方法的作用

public boolean dispatchTouchEvent(MotionEvent ev)这个方法的作用是对事件进行分发

public boolean onInterceptTouchEvent(MotionEvent ev)对事件进行拦截

public boolean onTouchEvent(MotionEvent event) 处理事件

 

为什么要进行事件分发呢?

分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

 

onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

这三个方法会同时出现在viewGroup里,但是在view里少了一个onInterceptTouchEvent方法,这个也不难理解,肯定是因为touch一个View才产生的事件,View是最底层

所以没必要进行拦截了肯定要进行处理了。

 

了解了这些,那么想事件究竟怎么分发呢?我觉得分发分为三个步骤,   开始---》寻找处理事件的view---》处理

让我们来看下事件究竟从哪里开始?

重写两个ViewGroup继承LinearLayout(这个随便继承),分别是GroupA,和GroupB,然后重写事件有关的三个方法,并进行log的打印。

package com.example.view;

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

public class GroupA extends LinearLayout {

    public GroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        setWillNotDraw(false);
    }
    //事件分发
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        Log.v("xys","GroupA-----dispatchTouchEvent"+ev.getAction());
        return super.dispatchTouchEvent(ev);
    }
    //事件拦截
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        Log.v("xys","GroupA-----onInterceptTouchEvent"+ev.getAction());
        //如果返回true说明进行拦截,从而不会往下传递而是自己进行处理
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.v("xys","GroupA-----onTouchEvent"+event.getAction());
        return super.onTouchEvent(event);
    }
}
package com.example.view;

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

public class GroupB extends LinearLayout {

    public GroupB(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        setWillNotDraw(false);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        getParent().requestDisallowInterceptTouchEvent(true);
        Log.v("xys","GroupB-----dispatchTouchEvent"+ev.getAction());
        //请求父控件不拦截事件
        return super.dispatchTouchEvent(ev);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        Log.v("xys","GroupB-----onInterceptTouchEvent"+ev.getAction());
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.v("xys","GroupB-----onTouchEvent"+event.getAction());
        return super.onTouchEvent(event);
    }
}

最后一个是View,它没有拦截的方法

package com.example.view;

import com.example.study.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;

public class MyView extends TextView {

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public MyView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.v("xys","MyView-----onTouchEvent"+event.getAction());
        return super.onTouchEvent(event);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.v("xys","MyView-----dispatchTouchEvent"+event.getAction());
        return super.dispatchTouchEvent(event);
    }
}

然后在布局文件里进行布局

<com.example.view.GroupA
    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" 
    android:orientation="vertical"
    android:background="#345"
    >
<com.example.view.GroupB 
     android:layout_width="match_parent"
    android:layout_height="100dp"
    android:orientation="vertical"
    android:background="#f9f"
    >
    <com.example.view.MyView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="aaaaa"
        />
    
</com.example.view.GroupB>
</com.example.view.GroupA>

在这里我们可以看见最外层的布局是A,其次是B,最后是我们的View

运行打印的log是这样的。

我点击了下View,可以明显看出布局是从A开始进行传递,

开始先执行分发方法,然后进行拦截,

然后传递给B布局的分发方法,B的拦截方法,

最后传递给View,View没有拦截方法,

只能进行处理。

然后开始进行回传。

这个是没有一个事件进行处理。只是让一个事件放肆的传递。

 

但事实上我们并不需要这样,我们既然点击了View肯定是要帮我们做一些事情,我们让view对事件进行处理,看下log的打印情况

@Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        Log.v("xys","MyView-----onTouchEvent"+event.getAction());
        return true;
    }

我点击了View,可以很明显看见我们的View处理了事件,并且没有回传,那为什么会打印了这么多的Log呢?

因为我是点击了,点击由两个事件组成,down和up。所以会响应事件两次。

 

如果我们事件不在View,进行处理,而是在B里进行处理会有什么样的效果呢?  可以大胆猜想下,既然处理了就会终止事件传递,

那么肯定不会调用View的分发方法和处理方法了。我们打印log来验证。

点击了B,结果和我们想的一样,没有往下传递。

我们至此就明白了onTouchEvent()的返回值作用。

 

那我们现在设想一个场景,ViewPager可以左右滑动,然后有个左侧有个slideMenu,这时候ViewPager右滑动,就会发生滑动冲突,

怎么来解决呢?

首先我们来分析,我本来想让这个布局响应事件,而现在并没有响应,而是父布局消费了事件。

这时候我们要重写Viewpager的dispatchTouchEvent方法,并加上这样一句代码。

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //请求父控件不拦截事件        
        getParent().requestDisallowInterceptTouchEvent(true);
        Log.v("xys","GroupB-----dispatchTouchEvent"+ev.getAction());
        
        return super.dispatchTouchEvent(ev);
    }

这样就可以进行拦截了。

为此我们做个验证,在A进行事件处理。然后滑动B,会不会调用B的onTouch方法?

 

 

可以看出B并没有处理这个事件。这时候我们加上那句代码再试下。

可以看出我们进行处理之后就会调用自己的ontouch方法了。

posted @ 2016-04-28 18:16  aaddrrooiidd  阅读(209)  评论(0编辑  收藏  举报