天天看點

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了。