天天看點

Android開發之觸摸事件處理機制詳解

使用者的每次觸碰(onclick,onlongclick,onscroll,etc.)都是由一個action_down+n個action_move+1個action_up組成的,使用者觸碰必先有個action_down響應,使用者觸碰結束必然會有個action_up。(當然如果在途中被攔截,就可能不會有了!)那麼view是如何分發消息和攔截消息呢?

1.view及其子類都會有的兩個方法:

public boolean dispatchtouchevent(motionevent ev) 這個方法用來分發touchevent

public boolean ontouchevent(motionevent ev) 這個方法用來處理touchevent

2.特殊的view子類viewgroup則還有一個方法:

public boolean onintercepttouchevent(motionevent ev) 這個方法用來攔截touchevent

3.分發

dispatchtouchevent 收到觸碰,則向最外層的view傳遞消息,再向子層的view分發

4.攔截:

onintercepttouchevent 攔截傳回true表示要攔截消息,不要再向子view傳遞(這裡的子view不是繼承關系,而是包容關系)。傳回false則表示不攔截消息,可以繼續向下一層級的view傳遞消息,子view将可以dispatchtouchevent 收到觸碰消息再分發消息

5.消息處理:

ontouchevent 處理事件,攔截了消息,或者是最後一個收到消息的view調用此方法來處理事件,若傳回true,則表示正确接收并處理。若傳回false則表示沒有被處理,将向父view傳遞(這裡的父view不是繼承關系,而是包容關系)

參考文檔:

<a target="_blank" href="http://blog.csdn.net/liutao5757124/article/details/6097125">http://blog.csdn.net/liutao5757124/article/details/6097125</a>

首先,看android的官方文檔正解

onintercepttouchevent()與ontouchevent()的機制:

  1. down事件首先會傳遞到onintercepttouchevent()方法

  2. 如果該viewgroup的onintercepttouchevent()在接收到down事件處理完成之後return false,

  那麼後續的move, up等事件将繼續會先傳遞給該viewgroup,之後才和down事件一樣傳遞給最

  終的目标view的ontouchevent()處理

  3. 如果該viewgroup的onintercepttouchevent()在接收到down事件處理完成之後return true,

  那麼後續的move, up等事件将不再傳遞給onintercepttouchevent(),而是和down事件一樣

  傳遞給該viewgroup的ontouchevent()處理,注意,目标view将接收不到任何事件。

  4. 如果最終需要處理事件的view的ontouchevent()傳回了false,那麼該事件将被傳遞至其上一

  層次的view的ontouchevent()處理

  5. 如果最終需要處理事件的view 的ontouchevent()傳回了true,那麼後續事件将可以繼續傳遞

  給該view的ontouchevent()處理

這是官方文檔的說法,要是自己沒親自去寫個程式觀察哈,基本上沒法了解,是以上程式先,然後分析:

布局檔案main.xml

Android開發之觸摸事件處理機制詳解

&lt;span style="font-size: small;"&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;  

&lt;com.hao.layoutview1 xmlns:android="http://schemas.android.com/apk/res/android"  

    android:orientation="vertical" android:layout_width="fill_parent"  

    android:layout_height="fill_parent"&gt;  

    &lt;com.hao.layoutview2  

        android:orientation="vertical" android:layout_width="fill_parent"  

        android:layout_height="fill_parent" android:gravity="center"&gt;  

        &lt;com.hao.mytextview  

            android:layout_width="wrap_content" android:layout_height="wrap_content"  

            android:id="@+id/tv" android:text="ab" android:textsize="40sp"  

            android:textstyle="bold" android:background="#ffffff"  

            android:textcolor="#0000ff" /&gt;  

    &lt;/com.hao.layoutview2&gt;  

&lt;/com.hao.layoutview1&gt;   

&lt;/span&gt;  

 第一層自定義布局layoutview1.java

Android開發之觸摸事件處理機制詳解

&lt;span style="font-size: small;"&gt;package com.hao;  

import android.content.context;  

import android.util.attributeset;  

import android.util.log;  

import android.view.motionevent;  

import android.widget.linearlayout;  

public class layoutview1 extends linearlayout {  

     private final string tag = "layoutview1";   

     public layoutview1(context context, attributeset attrs) {   

         super(context, attrs);   

         log.e(tag,tag);   

     }  

     @override   

     public boolean onintercepttouchevent(motionevent ev) {   

         int action = ev.getaction();   

         switch(action){   

         case motionevent.action_down:   

              log.e(tag,"onintercepttouchevent action:action_down");   

//              return true; 在這就攔截了,後面的就不會得到事件  

              break;  

        case motionevent.action_move:   

              log.e(tag,"onintercepttouchevent action:action_move");   

              break;   

         case motionevent.action_up:   

              log.e(tag,"onintercepttouchevent action:action_up");   

         case motionevent.action_cancel:   

              log.e(tag,"onintercepttouchevent action:action_cancel");   

         }   

         return false;   

     }   

     public boolean ontouchevent(motionevent ev) {   

              log.e(tag,"ontouchevent action:action_down");   

         case motionevent.action_move:   

              log.e(tag,"ontouchevent action:action_move");   

              log.e(tag,"ontouchevent action:action_up");   

              log.e(tag,"ontouchevent action:action_cancel");   

         return true;   

//         return false;  

     protected void onlayout(boolean changed, int l, int t, int r, int b) {   

         // todo auto-generated method stub   

         super.onlayout(changed, l, t, r, b);   

     protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {   

         super.onmeasure(widthmeasurespec, heightmeasurespec);   

}&lt;/span&gt;  

 第二層布局layoutview2.java

Android開發之觸摸事件處理機制詳解

public class layoutview2 extends linearlayout {  

    private final string tag = "layoutview2";   

    public layoutview2(context context, attributeset attrs) {   

       super(context, attrs);   

       log.e(tag,tag);   

    }   

    @override   

    public boolean onintercepttouchevent(motionevent ev) {   

       int action = ev.getaction();   

       switch(action){   

       case motionevent.action_down:   

           log.e(tag,"onintercepttouchevent action:action_down");  

//           return true;  

           break;   

       case motionevent.action_move:   

           log.e(tag,"onintercepttouchevent action:action_move");   

       case motionevent.action_up:   

           log.e(tag,"onintercepttouchevent action:action_up");   

       case motionevent.action_cancel:   

           log.e(tag,"onintercepttouchevent action:action_cancel");   

       }   

       return false;   

    public boolean ontouchevent(motionevent ev) {   

           log.e(tag,"ontouchevent action:action_down");   

           log.e(tag,"ontouchevent action:action_move");   

           log.e(tag,"ontouchevent action:action_up");   

           log.e(tag,"ontouchevent action:action_cancel");   

//       return true;   

       return false;  

}  

 自定義mytextview.java

Android開發之觸摸事件處理機制詳解

import android.view.view;  

import android.widget.textview;  

public class mytextview extends textview {  

    private final string tag = "mytextview";   

    public mytextview(context context, attributeset attrs) {   

//       return true;  

    public void onclick(view v) {   

       log.e(tag, "onclick");   

    public boolean onlongclick(view v) {   

       log.e(tag, "onlongclick");   

 其實代碼很簡單,就是自定義了view,在view裡面都重寫了intercepttouchevnet (),和ontouchevent(),然後測試其傳回值,對監聽的影響,關鍵是自己動手,逐個測試,并預測結果,等你能預測結果的時候,也就懂了,需要修改的地方就是intercepttouchevnet 和ontouchevent的傳回值,他們決定了事件監聽的流程,下面我畫了一張圖,如有不足之處歡迎指正,謝謝!

下面是我的正解:

基本的規則是: 

      *1.down事件首先會傳遞到onintercepttouchevent()方法 

      * 

      * 2.如果該viewgroup的onintercepttouchevent()在接收到down事件處理完成之後return false(不攔截),

      * 那麼後續的move, up等事件将繼續會先傳遞給該viewgroup,之後才和down事件一樣傳遞給最終的目标view的ontouchevent()處理。 

      *

      * 3.如果該viewgroup的onintercepttouchevent()在接收到down事件處理完成之後return true(攔截,那麼後面的move,up事件不需要在看因為已經攔截了, 我們直接拿去處理ontouchevent()就可以了),那麼後續的move, up等事件将不再傳遞給onintercepttouchevent(), 而是和down事件一樣傳遞給該viewgroup的ontouchevent()處理,注意,目标view将接收不到任何事件。

下面例子示範:

      * 1:layoutview1(31375): onintercepttouchevent action:action_down

      * 2:layoutview2(31375): onintercepttouchevent action:action_down

      * 3:layoutview2(31375): ontouchevent action:action_down

      * 4:layoutview1(31375): onintercepttouchevent action:action_move

      * 5:layoutview2(31375): ontouchevent action:action_move

      * 6:layoutview1(31375): onintercepttouchevent action:action_move

      * 7:layoutview2(31375): ontouchevent action:action_move

      * 8:layoutview1(31375): onintercepttouchevent action:action_up

      * 9:layoutview2(31375): ontouchevent action:action_up

      * 該設定為:

      * onintercepttouchevent:layoutview1為false,layoutview2為true

      * ontouchevent:layoutview2為true

      * 故而事件在layoutview2(onintercepttouchevent:傳回true)時被攔截并處理,根據上面說法就是layoutview2後續的move,up操作都不在經過onintercepttouchevent,直接

      * 交給ontouchevent處理,結果也的确如此。(見:layoutview2的3,5,7,9,第一次是onintercepttouchevent處理如1,以後交給ontouchevent)

      * 而layoutview1都還是要經過onintercepttouchevent(見layoutview1的4,6,8)

      * 4.如果最終需要處理事件的view的ontouchevent()傳回了false(沒能處理這個事件,不能丢在傳回來讓父繼續),

      * 那麼該事件将被傳遞至其上一層次的view的ontouchevent()處理。 

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

      * 感覺像是一個圈,然後一直在找一個能處理這個消息的人,如果找到了就結束,沒找到就循環,直到回到發出消息的那個人

      * 注(對下面):沒有标注的down表示攔截事件onintercepttouchevent,标注了ontouchevent就是處理事件

      * a.如果都沒處理(onintercepttouchevent傳回false):

a(down)--&gt;b(down)--&gt;c(ontouchevent down)--&gt;b(ontouchevent down)--&gt;a(ontouchevent down),沒有執行up事件,注意有move的話,在down和up之間,下面的都一樣。

      *b. b處理(b的onintercepttouchevent傳回true):

a(down)--&gt;b(down)--&gt;b(ontouchevent)--&gt;a(ontouchevent up)--&gt;b(ontouchevent up)--&gt;(over)

形象說明:如果父親不攔截消息就傳給兒子,如果兒子要這個消息就處理(down),結束,然後有父親1--父親2--兒子以此釋放消息(up)。 然是如果兒子對這個消息置之不理,那這個消息又傳回父親,由父親來處理即。

下面給出了5中情況(不攔截表示onintercepttouchevent傳回false):

      * 11** 父親1(layoutview1不攔截false)---父親2(layoutview2不攔截false)--兒子(mytextview,ontouchevent return true)--結束

      * 22** 父親1(layoutview1不攔截false)---父親2(layoutview2不攔截false)--兒子(mytextview,ontouchevent return false)--回傳給父親2(ontouchevent return true)--結束

      * 33** 父親1(layoutview1不攔截false)---父親2(layoutview2不攔截false)--兒子(mytextview,ontouchevent return false)--回傳給父親2(ontouchevent return false)--父親1(ontouchevent return true)--結束(如果都沒處理不在執行up action)

      * 44** 父親1(layoutview1攔截true)--父親1(ontouchevent return true)--結束          (down--down(ontouchevent)--up(ontouchevent))

      * 55** 父親1(layoutview1攔截false)--父親2(layoutview2攔截true)--父親2(ontouchevent return false)--父親1(ontouchevent return true)--結束      (down1--down2--down(2 ontouchevent)--down(1 ontouchevent)--up(1 ontouchevent))(1:父親2,2:父親2)

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

      * 5.如果最終需要處理事件的view 的ontouchevent()傳回了true,那麼後續事件将可以繼續傳遞給該view的ontouchevent()處理。 

      */

下面給出一張處理的流程圖:

Android開發之觸摸事件處理機制詳解

 下附源代碼:

下載下傳次數: 40

<a target="_blank" href="http://hao3100590.iteye.com/blog/1267294#">檢視圖檔附件</a>

繼續閱讀