Android事件傳遞機制

歡迎指教!!!

http://blog.csdn.net/hemeng2009/article/details/40076463

由於點擊屏幕空白處隱藏軟鍵盤中接觸到了dispatchtouchevent,onInterceptTouchEvent,ontouchevent,故而對這方面知識進行了詳細瞭解。

一、ViewViewGroup

 

所有的UI控件例如ButtonTextView都是繼承於View,而所有的佈局控件、容器控件繼承於ViewGroup

二、事件

       Android中,事件主要包括點按、長按、拖拽、滑動等,點按又包括單擊和雙擊,另外還包括單指操作和多指操作。所有這些都構成了Android中的事件響應。總的來說,所有的事件都由如下三個部分作爲基礎:

·       按下(ACTION_DOWN

·       移動(ACTION_MOVE

·       擡起(ACTION_UP

三、事件響應函數(回調函數)

View.java                                                                        ViewGroup.java

public booleandispatchTouchEvent(MotionEvent event) public boolean dispatchTouchEvent(MotionEvent event)

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

我們可以看出ViewGroup多了一個onInterceptTouchEvent方法,返回值均爲boolean

      返回值: 事件傳遞,傳遞的過程就是一個接一個,具體就是根據響應函數的返回值(true/false)判斷事件是否繼續傳遞下去。在Android中,所有的事件都是從開始經過傳遞到完成事件的消費,這些方法的返回值決定了事件------繼續往下傳/還是被攔截了/被消費了。

       參:MotionEvent繼承於InputEvent,用於標記各種動作事件。之前提到的ACTIONDOWNACTIONMOVEACTION_UP都是MotinEvent中定義的常量。我們通過MotionEvent傳進來的事件類型來判斷接收的是哪一種類型的事件。

       能:

·       dispatchTouchEvent方法用於事件的分發,Android中所有的事件都必須經過這個方法的分發,然後決定是自身消費當前事件還是繼續往下分發給子控件處理。返回true表示不繼續分發,事件被消費。返回false則繼續往下分發,如果是ViewGroup則分發給onInterceptTouchEvent進行判斷是否攔截該事件。

·       onTouchEvent方法用於事件的處理,返回true表示消費處理當前事件,返回false則不處理,交給子控件進行繼續分發。

·       onInterceptTouchEventViewGroup中才有的方法,View中沒有,它的作用是負責事件的攔截,返回true的時候表示攔截當前事件,不繼續往下分發,交給自身的onTouchEvent進行處理。返回false則不攔截,繼續往下傳。這是ViewGroup特有的方法,因爲ViewGroup中可能還有子View,而在AndroidView中是不能再包含子View的(iOS可以)。

四、應用

例子關注與不同組件的調用順序,實際中根據需要選擇。

view:

新建一個類RTButton繼承Button,用來實現我們對按鈕事件的跟蹤。

RTButton.java

public classRTButton extends Button {

        public RTButton(Context context,AttributeSet attrs) {

               super(context, attrs);

        }

        //基於回調函數實現組件的方法重載(2個)

        @Override

        public boolean dispatchTouchEvent(MotionEvent event) {

               switch (event.getAction()) {

               //下面用****************************代替switch內容

                               caseMotionEvent.ACTION_DOWN:

                                         System.out.println("RTButton---onTouch---DOWN");

                                         break;

                                 caseMotionEvent.ACTION_MOVE:

                                         System.out.println("RTButton---onTouch---MOVE");

                                         break;

                                 caseMotionEvent.ACTION_UP:

                                        System.out.println("RTButton---onTouch---UP");

                                         break;

                                 default:

                                         break;

 

               }

               returnsuper.dispatchTouchEvent(event);

        }

        @Override

        public boolean onTouchEvent(MotionEventevent) {

               switch (event.getAction()) {

               ******************************

               }

               return super.onTouchEvent(event);

        }

}

activity_main.xml在根佈局下放入自定義的按鈕RTButton

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/myLayout"

   android:layout_width="match_parent"

   android:layout_height="match_parent" >

    <com.ryantang.eventdispatchdemo.RTButton

        android:id="@+id/btn"

       android:layout_width="match_parent"

       android:layout_height="wrap_content"

        android:text="Button"/>

</LinearLayout>

 

Activity

Activity中爲RTButton設置onTouchonClick的監聽器來跟蹤事件傳遞的過程,另外,Activity中也有一個dispatchTouchEvent方法和一個onTouchEvent方法,重寫他們並輸出打印信息。

MainActivity.java

public classMainActivity extends Activity {

        private RTButton button;

        @Override

        protected void onCreate(BundlesavedInstanceState) {

                 super.onCreate(savedInstanceState);

                 setContentView(R.layout.activity_main);

                 button =(RTButton)this.findViewById(R.id.btn);

//完全可以做一個layout的監聽,對layout加入id,然後和button相同即可,參看

//http://blog.csdn.net/hemeng2009/article/details/40076463

                 //基於button監聽實現事件響應

                 button.setOnTouchListener(newOnTouchListener() {

//onTouch方法

                         @Override

                         public boolean onTouch(View v, MotionEvent event) {

                                  switch(event.getAction()) {

                                  ***************************

                                  }

                                  return false;

                         }

                 });

                 //click事件監聽

                 button.setOnClickListener(newOnClickListener() {

                         @Override

                         public voidonClick(View v) {

                                  System.out.println("RTButtonclicked!");

                         }

                 });    

        }

 

        //activity中的倆個方法----適用於全屏操作

        @Override

        publicboolean dispatchTouchEvent(MotionEvent event) {

                 switch(event.getAction()) {

                 *************************************

                 }

                 returnsuper.dispatchTouchEvent(event);

        }

        @Override

        publicboolean onTouchEvent(MotionEvent event) {

                 switch(event.getAction()) {

                 ************************************

                 }

                 returnsuper.onTouchEvent(event);

        }

}

點擊按鈕--結果:

首先執行了ActivitydispatchTouchEvent方法進行事件分發,dispatchTouchEvent方法的返回值是super.dispatchTouchEvent(event),因此調用了父類方法,我們進入Activity.java的源碼中看看具體實現。

dispatchTouchEvent方法開始只處理了ACTIONDOWN事件,因爲一個事件以down爲開始。

//////////////////////////////////////////////////////

Android事件攔截

    

Android嵌套佈局事件傳遞

嵌套佈局:RTLayout(ViewGroup)繼承於LinearLayout,重寫dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent方法。

那麼,事件是先傳遞到View呢,還是先傳遞到ViewGroup的?

RTLayout.java

public class RTLayout extends LinearLayout {

        public RTLayout(Context context,AttributeSet attrs) {

                 super(context, attrs);

        }

        @Override

        public booleandispatchTouchEvent(MotionEvent event) {

                 switch (event.getAction()) {

                ********************************************

                 }

                 returnsuper.dispatchTouchEvent(event);

        }

        @Override

        public boolean onInterceptTouchEvent(MotionEvent event) {

                 switch (event.getAction()) {

               *********************************************

                 }

                 returnsuper.onInterceptTouchEvent(event);

        }

        @Override

        public boolean onTouchEvent(MotionEventevent) {

                 switch (event.getAction()) {

                ************************************************

                 }

                 returnsuper.onTouchEvent(event);

        }

}

同時,在佈局文件中爲RTButton添加一個父佈局,指明爲自定義的RTLayout,修改後的佈局文件如下。

activity_main.xml

<LinearLayoutxmlns: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.ryantang.eventdispatchdemo.RTLayout

        android:id="@+id/myLayout"

       android:layout_width="match_parent"

       android:layout_height="match_parent" >

        <com.ryantang.eventdispatchdemo.RTButton

            android:id="@+id/btn"

           android:layout_width="match_parent"

           android:layout_height="wrap_content"

            android:text="Button"/>

   </com.ryantang.eventdispatchdemo.RTLayout>

</LinearLayout>


最後,我們在Activity中也爲RTLayout設置onTouchonClick事件,在MainActivity中添加如下代碼。

MainActivity.java

//採用的事件監聽的方法

//同樣可以爲主layout監聽。

        rtLayout.setOnTouchListener(newOnTouchListener() {

                         @Override

                         public booleanonTouch(View v, MotionEvent event) {

                                  switch(event.getAction()) {

                                  ***************************************

                                  }

                                  return false;

                         }

                 });         

        rtLayout.setOnClickListener(newOnClickListener() {          

                         @Override

                         public voidonClick(View v) {

                                  System.out.println("RTLayoutclicked!");

                         }

                 });

代碼修改完畢後,編譯運行工程,同樣,點擊按鈕,查看日誌輸出結果如下:


從日誌輸出結果我們可以看到,嵌套了RTLayout以後,事件傳遞的順序變成了Activity->RTLayout->RTButton,這也就回答了前面提出的問題,Android中事件傳遞是從ViewGroup傳遞到View的,而不是反過來傳遞的。

從輸出結果第三行可以看到,執行了RTLayoutonInterceptTouchEvent方法,該方法的作用就是判斷是否需要攔截事件,我們到ViewGroup的源碼中看看該方法的實現。

ViewGroup.java

public boolean onInterceptTouchEvent(MotionEvent ev) {

        return false;

    }

該方法的實現很簡單,只返回了一個false如果onInterceptTouchEvent返回false則不攔截,如果返回true則攔截當前事件。

 

總結

現總結如下:

        根據上面的內容,我們知道,點擊屏幕空白處,隱藏鍵盤,實現對佈局文件的監聽即可。

·       Android中事件傳遞按照從上到下進行層級傳遞,事件處理從Activity開始到ViewGroup再到View

·       事件傳遞方法包括dispatchTouchEventonTouchEventonInterceptTouchEvent,其中前兩個是ViewViewGroup都有的,最後一個是隻有ViewGroup纔有的方法。這三個方法的作用分別是負責事件分發、事件處理、事件攔截。

·       onTouch事件要先於onClick事件執行,onTouch在事件分發方法dispatchTouchEvent中調用,而onClick在事件處理方法onTouchEvent中被調用,onTouchEvent要後於dispatchTouchEvent方法的調用。

·       ps:http://www.infoq.com/cn/articles/android-event-delivery-mechanism

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章