Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之觸摸事件處理機制詳解

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

編輯:關於Android編程

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不是繼承關系,而是包容關系)


Android事件模型之interceptTouchEvnet ,onTouchEvent關系正解

參考文檔:

http://blog.csdn.net/liutao5757124/article/details/6097125


首先,看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

Java代碼 收藏代碼
  1. android:orientation="vertical" android:layout_width="fill_parent"
  2. android:layout_height="fill_parent">
  3. android:orientation="vertical" android:layout_width="fill_parent"
  4. android:layout_height="fill_parent" android:gravity="center">
  5. android:layout_width="wrap_content" android:layout_height="wrap_content"
  6. android:id="@+id/tv" android:text="AB" android:textSize="40sp"
  7. android:text android:background="#FFFFFF"
  8. android:textColor="#0000FF" />
  9. 第一層自定義布局LayoutView1.java

    Java代碼 收藏代碼
    1. package com.hao;
    2. import android.content.Context;
    3. import android.util.AttributeSet;
    4. import android.util.Log;
    5. import android.view.MotionEvent;
    6. import android.widget.LinearLayout;
    7. public class LayoutView1 extends LinearLayout {
    8. private final String TAG = "LayoutView1";
    9. public LayoutView1(Context context, AttributeSet attrs) {
    10. super(context, attrs);
    11. Log.e(TAG,TAG);
    12. }
    13. @Override
    14. public boolean onInterceptTouchEvent(MotionEvent ev) {
    15. int action = ev.getAction();
    16. switch(action){
    17. case MotionEvent.ACTION_DOWN:
    18. Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
    19. // return true; 在這就攔截了,後面的就不會得到事件
    20. break;
    21. case MotionEvent.ACTION_MOVE:
    22. Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE");
    23. break;
    24. case MotionEvent.ACTION_UP:
    25. Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP");
    26. break;
    27. case MotionEvent.ACTION_CANCEL:
    28. Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");
    29. break;
    30. }
    31. return false;
    32. }
    33. @Override
    34. public boolean onTouchEvent(MotionEvent ev) {
    35. int action = ev.getAction();
    36. switch(action){
    37. case MotionEvent.ACTION_DOWN:
    38. Log.e(TAG,"onTouchEvent action:ACTION_DOWN");
    39. break;
    40. case MotionEvent.ACTION_MOVE:
    41. Log.e(TAG,"onTouchEvent action:ACTION_MOVE");
    42. break;
    43. case MotionEvent.ACTION_UP:
    44. Log.e(TAG,"onTouchEvent action:ACTION_UP");
    45. break;
    46. case MotionEvent.ACTION_CANCEL:
    47. Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");
    48. break;
    49. }
    50. return true;
    51. // return false;
    52. }
    53. @Override
    54. protected void onLayout(boolean changed, int l, int t, int r, int b) {
    55. // TODO Auto-generated method stub
    56. super.onLayout(changed, l, t, r, b);
    57. }
    58. @Override
    59. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    60. // TODO Auto-generated method stub
    61. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    62. }
    63. }

      第二層布局LayoutView2.java

      Java代碼 收藏代碼
      1. package com.hao;
      2. import android.content.Context;
      3. import android.util.AttributeSet;
      4. import android.util.Log;
      5. import android.view.MotionEvent;
      6. import android.widget.LinearLayout;
      7. public class LayoutView2 extends LinearLayout {
      8. private final String TAG = "LayoutView2";
      9. public LayoutView2(Context context, AttributeSet attrs) {
      10. super(context, attrs);
      11. Log.e(TAG,TAG);
      12. }
      13. @Override
      14. public boolean onInterceptTouchEvent(MotionEvent ev) {
      15. int action = ev.getAction();
      16. switch(action){
      17. case MotionEvent.ACTION_DOWN:
      18. Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
      19. // return true;
      20. break;
      21. case MotionEvent.ACTION_MOVE:
      22. Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE");
      23. break;
      24. case MotionEvent.ACTION_UP:
      25. Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP");
      26. break;
      27. case MotionEvent.ACTION_CANCEL:
      28. Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL");
      29. break;
      30. }
      31. return false;
      32. }
      33. @Override
      34. public boolean onTouchEvent(MotionEvent ev) {
      35. int action = ev.getAction();
      36. switch(action){
      37. case MotionEvent.ACTION_DOWN:
      38. Log.e(TAG,"onTouchEvent action:ACTION_DOWN");
      39. break;
      40. case MotionEvent.ACTION_MOVE:
      41. Log.e(TAG,"onTouchEvent action:ACTION_MOVE");
      42. break;
      43. case MotionEvent.ACTION_UP:
      44. Log.e(TAG,"onTouchEvent action:ACTION_UP");
      45. break;
      46. case MotionEvent.ACTION_CANCEL:
      47. Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");
      48. break;
      49. }
      50. // return true;
      51. return false;
      52. }
      53. }
      54. 自定義MyTextView.java

        Java代碼 收藏代碼
        1. package com.hao;
        2. import android.content.Context;
        3. import android.util.AttributeSet;
        4. import android.util.Log;
        5. import android.view.MotionEvent;
        6. import android.view.View;
        7. import android.widget.TextView;
        8. public class MyTextView extends TextView {
        9. private final String TAG = "MyTextView";
        10. public MyTextView(Context context, AttributeSet attrs) {
        11. super(context, attrs);
        12. Log.e(TAG,TAG);
        13. }
        14. @Override
        15. public boolean onTouchEvent(MotionEvent ev) {
        16. int action = ev.getAction();
        17. switch(action){
        18. case MotionEvent.ACTION_DOWN:
        19. Log.e(TAG,"onTouchEvent action:ACTION_DOWN");
        20. break;
        21. case MotionEvent.ACTION_MOVE:
        22. Log.e(TAG,"onTouchEvent action:ACTION_MOVE");
        23. break;
        24. case MotionEvent.ACTION_UP:
        25. Log.e(TAG,"onTouchEvent action:ACTION_UP");
        26. break;
        27. case MotionEvent.ACTION_CANCEL:
        28. Log.e(TAG,"onTouchEvent action:ACTION_CANCEL");
        29. break;
        30. }
        31. return false;
        32. // return true;
        33. }
        34. public void onClick(View v) {
        35. Log.e(TAG, "onClick");
        36. }
        37. public boolean onLongClick(View v) {
        38. Log.e(TAG, "onLongClick");
        39. return false;
        40. }
        41. }
        42. 其實代碼很簡單,就是自定義了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)-->B(DOWN)-->C(onTouchEvent DOWN)-->B(onTouchEvent DOWN)-->A(onTouchEvent DOWN),沒有執行UP事件,注意有MOVE的話,在DOWN和UP之間,下面的都一樣。 *b. B處理(B的onInterceptTouchEvent返回true): A(DOWN)-->B(DOWN)-->B(onTouchEvent)-->A(onTouchEvent UP)-->B(onTouchEvent UP)-->(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()處理。 */
          下面給出一張處理的流程圖:
          \
          下附源代碼:
          • TouchEventTest.rar (94.3 KB)
          • 下載次數: 40
            • 查看圖片附件
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved