天天看点

android Event(事件传递)(编辑中)

Android事件构成

在android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括点击、双击、按下、弹起等,另外还包括单指操作和多指操作。所有这些都构成android的事件响应。总的来说,所有的时间都由如下三个部分做为基础:

按下(ACTION_DOWN)

移动(ACTION_MOVE)

抬起(ACTION_UP)

取消(ACHTION_CANCEL)

所有的操作事件首先必须执行的是按下操作(ACTION_DOWN),之后所有的操作都是以按下操作作为前提,当按下操作完成以后,接下来可能是一段移动(ACTION_MOVE)然后就是抬起(ACTION_UP),或者是按下操作执行完成后没有移动就直接抬起。这一系列的动作(ACTION)在android中都可以进行控制或者拦截。

我们知道。所有的事件操作都发生在触摸屏上,而在屏幕上与我们交互的就是各种各样的视图组建(View),在Android中,所有的视图都继承与View,另外通过各种布局组建(ViewGroup)来对View进行布局,ViewGroup也是继承与View。所有的UI控件例如Button、TextView都是继承与View,而所有的布局控件例如LinearLayout、容器控件GridView都是继承于ViewGroup。所以我们的event主要是发送在view和ViewGroup之间,那么view和ViewGroup中主要有哪些方法来对事件进行相应呢?

我们可以再view和ViewGroup的源码中找到如下几个方法:

View.java

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
           

ViewGroup.java

public boolean dispatchTouchEvent(MotionEvent event)
public boolean onTouchEvent(MotionEvent event)
public boolean onInterceptTouchEvent(MotionEvent event)
           

在View和ViewGroup中都存在dispatchTouchEvent和onTouchEvent方法,但是在ViewGroup中还存在另一个View没有的onInterceptTouchEvent方法,那些方法都是做什么的呢?我们首先先看看他们的返回值。这些方法的返回值都是Boolean型,为什么都返回的是Boolean型的呢,因为在android机制中有个东西叫做“事件传递”传递的过程就是一个接着一个,那到了某个点后是否继续往下传递呢?你发现了么?这里用到了“是否”两字,这个就是决定哪些方法应该用Boolean来作为传递的返回值,这些方法的返回值就决定这个事件是否继续传递下去,或者被拦截,或者被自己消费了。

接下来就这些方法的参数做个说明,都接受了一个MotionEvent类型的参数,MotionEvent继承于InputEvent,用于识别各种动作(action_down、action_up、action_move、cation_cancel…..),这些动作都是在MotionEvent中定义的常量。我们通过MotionEvent传递进来的时间类型来进行判断是那种类型的事件。好了。传递参数和返回值都弄清楚了,那我们来了解下什么时候出发什么事件:

  • dispatchTouchEvent方法用于事件的分发,android中所有的事件都必须经过这个方法的分发,然后决定是自身消费,还是继续分发下去给子控件处理。返回true表示不继续分发,事件没有被消费。返回false则继续往下分发下去,如果是ViewGroup则分发给onInterceptTouchEvent进行判断拦截是否处理该事件,如果这里是View则分发就直接传递给onTouchEvent处理。
  • onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件继续分发。
  • onInterceptTouchEvent是ViewGroup中才有的方法,Vew中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,直接交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传,这是ViewGroup特有方法,因为ViewGroup中可能还有子的View,而View中就不再包含View的。所以这个方法就是单独处理可能出现ViewGroup中还继续包含View。

到目前为止,android中事件的构成以及事件处理方法的作用你应该比较清楚。下面我们用个简单的Demo做个演示。

Android事件处理

首先我们先创建一个工程,创建几个类:分别继承不同的View。(myViewGroup继承ViewGroup、myView继承View、myButton继承Button);

首先我们看布局文件xml:

<RelativeLayout 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" >

    <com.example.android_event.myViewGroup
        android:id="@+id/myViewGroup"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:orientation="vertical">

        <com.example.android_event.myButton
            android:id="@+id/mybtn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="button" />
        <com.example.android_event.myView 
            android:id="@+id/myView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#124567"/>
    </com.example.android_event.myViewGroup>

</RelativeLayout>
           
android Event(事件传递)(编辑中)

在看下myViewGroup继承ViewGroup用来探测和追踪ViewGroup对事件的处理:

public class myViewGroup extends LinearLayout{
    private static  final String TAG = "xj_myViewGroup";
    public myViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "myViewGroup dispatchTouchEvent default return"+super.onTouchEvent(ev));
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.d(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_DOWN"+MotionEvent.ACTION_DOWN);
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_MOVE"+MotionEvent.ACTION_MOVE);
            break;
        case MotionEvent.ACTION_UP:
            Log.d(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_UP"+MotionEvent.ACTION_UP);
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.d(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_CANCEL"+MotionEvent.ACTION_CANCEL);
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(ev);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        ev.getAction();
        Log.d(TAG, "myViewGroup onInterceptTouchEvent default return"+super.onTouchEvent(ev));
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.d(TAG, "onInterceptTouchEvent===>MotionEvent.ACTION_DOWN"+MotionEvent.ACTION_DOWN);
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d(TAG, "onInterceptTouchEvent===>MotionEvent.ACTION_MOVE"+MotionEvent.ACTION_MOVE);
            break;
        case MotionEvent.ACTION_UP:
            Log.d(TAG, "onInterceptTouchEvent===>MotionEvent.ACTION_UP"+MotionEvent.ACTION_UP);
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.d(TAG, "onInterceptTouchEvent===>MotionEvent.ACTION_CANCEL"+MotionEvent.ACTION_CANCEL);
            break;
        default:
            break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "myViewGroup onTouchEvent default return"+super.onTouchEvent(event));
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.d(TAG, "onTouchEvent===>MotionEvent.ACTION_DOWN"+MotionEvent.ACTION_DOWN);
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d(TAG, "onTouchEvent===>MotionEvent.ACTION_MOVE"+MotionEvent.ACTION_MOVE);
            break;
        case MotionEvent.ACTION_UP:
            Log.d(TAG, "onTouchEvent===>MotionEvent.ACTION_UP"+MotionEvent.ACTION_UP);
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.d(TAG, "onTouchEvent===>MotionEvent.ACTION_CANCEL"+MotionEvent.ACTION_CANCEL);
            break;
        default:
            break;
        }
        return super.onTouchEvent(event);
    }

}
           

接下来就是myButton继承了button:

public class myButton extends Button{
    private static final String TAG = "xj_myButton";
    public myButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.v(TAG, "MyButton dispatchTouchEvent.");  
        Log.v(TAG, "MyButton dispatchTouchEvent default return "  + super.onTouchEvent(event));  
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.v(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_DOWN"+MotionEvent.ACTION_DOWN);
            break;
        case MotionEvent.ACTION_MOVE:
            Log.v(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_MOVE"+MotionEvent.ACTION_MOVE);
            break;
        case MotionEvent.ACTION_UP:
            Log.v(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_UP"+MotionEvent.ACTION_UP);
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.v(TAG, "dispatchTouchEvent===>MotionEvent.ACTION_CANCEL"+MotionEvent.ACTION_CANCEL);
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(event);
    }

     @Override  
     public boolean onTouchEvent(MotionEvent event) {  
        // TODO Auto-generated method stub  
        switch(event.getAction()){  
        case MotionEvent.ACTION_DOWN:  
            Log.v(TAG, "MyButton-onTouchEvent-ACTION_DOWN...");  
            break; 
        case MotionEvent.ACTION_UP:  
            Log.v(TAG, "MyButton-onTouchEvent-ACTION_UP...");  
            break;  
       default:break;  
            }  
       return super.onTouchEvent(event);  
     }  

}
           

上面的代码先到此(如果这里先忽略下面的myView稍后在说明),我们将项目先跑起来。当我们点击button会出现什么效果呢?我们来看下logcat:

android Event(事件传递)(编辑中)

通过日志输出我们可以看到,在这个事件中首先执行ViewGroup的dispatchTouchEvent方法进行事件的分发,dispatchTouchEvent方法的返回值是super.dispatchTouchEvent(event)。这个时候我们会发现返回结果是false,前面也是到过,如果dispatchTouchEvent返回结果是false就不做处理然后继续分发到onInterceptTouchEvent,这个时候onInterceptTouchEvent也没有做处理,继续分发到View也就是到了button了。这个时候也是先由dispatchTouchEvent对事件进行拦截处理。看日志可以返现这个时候返回的结果是true,也就表示这个事件已经被View的dispatchTouchEvent拦截下来了。(这里View的dispatchTouchEvent和onTouchEvent都可以处理到由ViewGroup传递过来的事件)这里就是事件分发由ViewGroup到View的分发。

我们在来看下View.java

public class myView extends View{
    private static final String TAG = "jx_MyView";
    public myView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "MyView onTouchEvent default return "  + super.onTouchEvent(event));  
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG, "MyView onTouchEvent MotionEvent.ACTION_DOWN =>"  + MotionEvent.ACTION_DOWN);  
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG, "MyView onTouchEvent MotionEvent.ACTION_MOVE => "  + MotionEvent.ACTION_MOVE);  
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG, "MyView onTouchEvent MotionEvent.ACTION_UP => "  + MotionEvent.ACTION_UP);  
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.e(TAG, "MyView onTouchEvent MotionEvent.ACTION_CANCEL => "  + MotionEvent.ACTION_CANCEL);  
            break;
        default:
            break;
        }
        return super.onTouchEvent(event);
    } 
}
           

在看看下如果触碰button下面的View层会出现什么样的结果:

android Event(事件传递)(编辑中)

看到这个输出结果大家可以看出其实这里都没有对事件做处理,事件由ViewGroup到view(View并没有处理)然后又返回到ViewGroup了。这里没有做事件的拦截就继续分发,而View是包括ViewGroup的但是ViewGroup下面又可以包括View,最后还是回到ViewGroup了。