1. 程式人生 > >談談我對Android View事件分發的理解

談談我對Android View事件分發的理解

event 調用 ack 處理 group ans import ras 運行

寫這篇博客的緣由。近期因為項目中用到相似一個LinearLayout中水平布局中,有一個TextView和Button,然後對該LinearLayout布局設置點擊事件。點擊TextView能夠觸發該點擊事件。然而奇怪的是點擊Button卻不能觸發。

然後google到了解決的方法(重寫Button,然後重寫當中的ontouchEvent方法,且返回值為false)。可是不知道原因,這兩天看了幾位大神的博客,然後自己總結下。

public class MyButton extends Button {


    private final static String TAG = "MyButton::zjt"
; public MyButton(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public MyButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub
} @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouchEvent ACTION_MOVE"
); break; case MotionEvent.ACTION_UP: Log.e(TAG, "onTouchEvent ACTION_UP"); break; default: break; } //return super.onTouchEvent(event); return false; } @Override public boolean dispatchTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; default: break; } return super.dispatchTouchEvent(event); } }

MyTextView.java

public class MyTextView extends TextView {

    private final static String TAG = "MyTextView : ";

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:

            Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
            break;
        case MotionEvent.ACTION_MOVE:

            Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:

            Log.e(TAG, "dispatchTouchEvent ACTION_UP");
            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.e(TAG, "onTouchEvent ACTION_DOWN");
            ////return true 後面的ACTION_MOVE、和ACTION_UP能夠得以運行。假設不做不論什麽操作,即 break。因為textview默認是不可點擊和長點擊的,所以return false,
            //那麽 dispatcTouchEvent 會 return false,導致後面的ACTION_MOVE 和 ACTION_UP不能運行
            //return true;
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG, "onTouchEvent ACTION_MOVE");

            break;
        case MotionEvent.ACTION_UP:

            Log.e(TAG, "onTouchEvent ACTION_UP");
            break;

        default:
            break;
        }
        return super.onTouchEvent(event);
    }

}

MainActivity例如以下:

public class TestTouchActivity extends Activity {

    private final static String TAG = "TestTouchActivity";
    private Button mButton;
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        setContentView(R.layout.my_button_layout);
        mButton = (Button) findViewById(R.id.my_btn);
        mTextView = (TextView) findViewById(R.id.my_textview);

//      mTextView.setOnClickListener(new OnClickListener() {
//          
//          @Override
//          public void onClick(View v) {
//              // TODO Auto-generated method stub
//              Log.e(TAG, "mTextView onClick");
//          }
//      });

        mButton.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                int action = event.getAction();

                switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "onTouch ACTION_DOWN");
                    return true;
                    //break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "onTouch ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "onTouch ACTION_UP");
                    break;
                default:
                    break;
                }

                return false;
            }
        });

        mTextView.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                int action = event.getAction();

                switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "mTextView onTouch ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "mTextView onTouch ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "mTextView onTouch ACTION_UP");
                    break;
                default:
                    break;
                }

                return false;
            }
        });

    }
}

點擊Button和TextView的節目例如以下:

點擊Button:
技術分享

點擊TextView:
技術分享

為什麽結果是這種,參考博文已經寫得非常精彩了,我就站在巨人的肩膀上,總結下,我們從上面的結果能夠看出,當我們點擊屏幕上的View的時候首先觸發的是View的dispatchTouchEvent事件。源代碼例如以下:

/** 
 * Pass the touch screen motion event down to the target view, or this 
 * view if it is the target. 
 * 
 * @param event The motion event to be dispatched. 
 * @return True if the event was handled by the view, false otherwise. 
 */  
public boolean dispatchTouchEvent (MotionEvent event) {  
    if (mOnTouchListener != null && ( mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch( this, event)) {  
        return true;  
    }  
    return onTouchEvent(event);  
}  

上面的mOnTouchListener 就是我們在Activity中設置的Touch事件,我們設置的時候在onTouch中返回的是false,所以會接著運行以下的onTouchEvent方法,能夠看出onTouchEvent的返回值就是dispatchTouchEvent 的返回值。onTouchEvent這種方法源代碼比較長,我截斷了。

public boolean onTouchEvent(MotionEvent event) {  
        。

。。

。。。。。。。。 此處有省略 if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { 。。

。。。。

。。

。。

。 此處有省略 } return true; } return false; }

第4行就是推斷該View是否是可點擊或者可長按的。假設是返回true。在onTouchEvent中先運行ACTION_DOWN(手指按下)。假設返回true,那麽dispatchTouchEvent 的返回值也就是true。就能夠接著運行後面的ACTION_MOVE和ACTION_UP方法。假設返回false。那麽後面的ACTION_MOVE和ACTION_UP就不運行了,這個詳細原因我還不知道。假設有知道的能夠分享下。

說明 1:長按事件是在onTouchEvent中的ACTION_DOWN中觸發的(假設你設置了長按事件)。而點擊onclick事件是在ACTION_UP中觸發的。

如今分析下前面的樣例:

因為button默認是可點擊的,所以在onTouchEvent中會返回true,所以dispatchTouchEvent 也會返回true。後面的ACTION_MOVE和ACTION_UP能夠接著運行。
而TextView默認是不可點擊的所以onTouchEvent中會返回false,那麽dispatchTouchEvent 也會返回false,後面的ACTION_MOVE和ACTION_UP就運行不到了,和上面打印的log相符。

假設我們在Activity中對TextView設置onTouch事件返回true,結果會怎麽樣呢,我們先就著dispatchTouchEvent 的源代碼分析下:

mTextView.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return true;
            }
        });

因為返回true,我們從dispatchTouchEvent 源代碼的第10行能夠看出mOnTouchListener.onTouch( this, event))即返回true。那麽if條件就成立了,dispatchTouchEvent 直接返回true。接著運行後面的ACTION_MOVE和ACTION_UP,(ACTION_MOVE假設你點擊的時候滑動了才會運行)。可是後面的onTouchEvent就運行不到了。

log例如以下:

技術分享
沒有運行ACTION_MOVE是因為我高速點擊且沒有滑動,從log能夠看出運行完dispatchTouchEvent 的ACTION_DOWN之後又運行了dispatchTouchEvent 的ACTION_UP。

但並沒有運行onTouchEvent。

以下開始講主題了,也就是前言交代的問題。以下是我自己定義的ViewGroup:

public class MyLinearLayout extends LinearLayout {

    private final static String TAG = "MyLinearLayout :";

    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        int action = ev.getAction();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG, "dispatchTouchEvent , ACTION_DOWN");
            break;

        case MotionEvent.ACTION_MOVE:
            Log.e(TAG, "dispatchTouchEvent , ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG, "dispatchTouchEvent , ACTION_UP");
            break;
        default:
            break;
        }

        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        int action = ev.getAction();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG, "onInterceptTouchEvent , ACTION_DOWN");
            //return true;
            break;

        case MotionEvent.ACTION_MOVE:
            Log.e(TAG, "onInterceptTouchEvent , ACTION_MOVE");
            //return true;
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG, "onInterceptTouchEvent , ACTION_UP");
            break;
        default:
            break;
        }
        return super.onInterceptTouchEvent(ev);
        //return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        int action = event.getAction();

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG, "onTouchEvent , ACTION_DOWN");
            //return true;
            break;

        case MotionEvent.ACTION_MOVE:
            Log.e(TAG, "onTouchEvent , ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG, "onTouchEvent , ACTION_UP");
            break;
        default:
            break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        // TODO Auto-generated method stub
        Log.e(TAG, "enter requestDisallowInterceptTouchEvent");
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
    }

xml例如以下:


<?xml version="1.0" encoding="utf-8"?>
<com.example.test.view.touch.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/id_my_linearlayout"
     >

    <com.example.test.view.touch.MyButton 
        android:id="@+id/btn_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click me"
        />

     <com.example.test.view.touch.MyTextView
        android:id="@+id/my_textview_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="textview click"
        android:textSize="30sp"
        />

</com.example.test.view.touch.MyLinearLayout>

MainActivity例如以下:


package com.example.test.view.touch;

import com.example.drawview.R;

import android.app.Activity;
import android.os.Bundle;
import android.provider.Telephony.Mms;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class TestViewGroupeTouchActivity extends Activity {

    private final static String TAG = "TestViewGroupeTouchActivity : ";

    private Button mButton ;
    private TextView mTextView;
    private LinearLayout mLinearLayout ;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        setContentView(R.layout.my_linear_layout);

        mButton = (Button) findViewById(R.id.btn_click);
        mTextView = (TextView) findViewById(R.id.my_textview_click);
        mLinearLayout = (LinearLayout) findViewById(R.id.id_my_linearlayout);


        mLinearLayout.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Log.e(TAG, "mLinearLayout ,  onClick");
            }
        });

        mLinearLayout.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                int action = event.getAction();

                switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "mLinearLayout , onTouch ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "mLinearLayout , onTouch ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "mLinearLayout ,onTouch ACTION_UP");
                    break;
                default:
                    break;
                }
                return false;
            }
        });

        mButton.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                int action = event.getAction();

                switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "mButton onTouch ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "mButton onTouch ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "mButton onTouch ACTION_UP");
                    break;
                default:
                    break;
                }
                return false;
            }
        });

        mTextView.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "mTextView , onTouch ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "mTextView , onTouch ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "mTextView , onTouch ACTION_UP");
                    break;
                default:
                    break;
                }
                return false;
            }
        });

    }

}

說明2: 因為我是為了說明前言裏面的問題,所以ViewGroup的touch事件分發。我不作過多的說明。ViewGroup事件分發的流程是:dispatchTouchEvent–>onInterceptTouchEvent—>然後到手指點擊View的事件分發(參考上面所說的View的事件分發)。

onInterceptTouchEvent默認返回false,表示是否攔截事件。

ViewGroup的dispatchTouchEvent的源代碼例如以下:


[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
/** 
    * [email protected]} 
    */  
   @Override  
   public boolean dispatchTouchEvent(MotionEvent ev) {  
       final int action = ev.getAction();  
       final float xf = ev.getX();  
       final float yf = ev.getY();  
       final float scrolledXFloat = xf + mScrollX;  
       final float scrolledYFloat = yf + mScrollY;  
       final Rect frame = mTempRect;  

       //這個值默認是false, 然後我們能夠通過requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法  
       //來改變disallowIntercept的值  
       boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  

       //這裏是ACTION_DOWN的處理邏輯  
       if (action == MotionEvent.ACTION_DOWN) {  
        //清除mMotionTarget, 每次ACTION_DOWN都非常設置mMotionTarget為null  
           if (mMotionTarget != null) {  
               mMotionTarget = null;  
           }  

           //disallowIntercept默認是false, 就看ViewGroup的onInterceptTouchEvent()方法  
           if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
               ev.setAction(MotionEvent.ACTION_DOWN);  
               final int scrolledXInt = (int) scrolledXFloat;  
               final int scrolledYInt = (int) scrolledYFloat;  
               final View[] children = mChildren;  
               final int count = mChildrenCount;  
               //遍歷其子View  
               for (int i = count - 1; i >= 0; i--) {  
                   final View child = children[i];  

                   //假設該子View是VISIBLE或者該子View正在運行動畫, 表示該View才  
                   //能夠接受到Touch事件  
                   if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                           || child.getAnimation() != null) {  
                    //獲取子View的位置範圍  
                       child.getHitRect(frame);  

                       //如Touch到屏幕上的點在該子View上面  
                       if (frame.contains(scrolledXInt, scrolledYInt)) {  
                           // offset the event to the view‘s coordinate system  
                           final float xc = scrolledXFloat - child.mLeft;  
                           final float yc = scrolledYFloat - child.mTop;  
                           ev.setLocation(xc, yc);  
                           child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  

                           //調用該子View的dispatchTouchEvent()方法  
                           if (child.dispatchTouchEvent(ev))  {  
                               // 假設child.dispatchTouchEvent(ev)返回true表示  
                            //該事件被消費了。設置mMotionTarget為該子View  
                               mMotionTarget = child;  
                               //直接返回true  
                               return true;  
                           }  
                           // The event didn‘t get handled, try the next view.  
                           // Don‘t reset the event‘s location, it‘s not  
                           // necessary here.  
                       }  
                   }  
               }  
           }  
       }  

       //推斷是否為ACTION_UP或者ACTION_CANCEL  
       boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
               (action == MotionEvent.ACTION_CANCEL);  

       if (isUpOrCancel) {  
           //假設是ACTION_UP或者ACTION_CANCEL, 將disallowIntercept設置為默認的false  
        //假如我們調用了requestDisallowInterceptTouchEvent()方法來設置disallowIntercept為true  
        //當我們擡起手指或者取消Touch事件的時候要將disallowIntercept重置為false  
        //所以說上面的disallowIntercept默認在我們每次ACTION_DOWN的時候都是false  
           mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
       }  

       // The event wasn‘t an ACTION_DOWN, dispatch it to our target if  
       // we have one.  
       final View target = mMotionTarget;  
       //mMotionTarget為null意味著沒有找到消費Touch事件的View, 所以我們須要調用ViewGroup父類的  
       //dispatchTouchEvent()方法,也就是View的dispatchTouchEvent()方法  
       if (target == null) {  
           // We don‘t have a target, this means we‘re handling the  
           // event as a regular view.  
           ev.setLocation(xf, yf);  
           if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
               ev.setAction(MotionEvent.ACTION_CANCEL);  
               mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
           }  
           return super.dispatchTouchEvent(ev);  
       }  

       //這個if裏面的代碼ACTION_DOWN不會運行。僅僅有ACTION_MOVE  
       //ACTION_UP才會走到這裏, 假如在ACTION_MOVE或者ACTION_UP攔截的  
       //Touch事件, 將ACTION_CANCEL派發給target,然後直接返回true  
       //表示消費了此Touch事件  
       if (!disallowIntercept && onInterceptTouchEvent(ev)) {  
           final float xc = scrolledXFloat - (float) target.mLeft;  
           final float yc = scrolledYFloat - (float) target.mTop;  
           mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
           ev.setAction(MotionEvent.ACTION_CANCEL);  
           ev.setLocation(xc, yc);  

           if (!target.dispatchTouchEvent(ev)) {  
           }  
           // clear the target  
           mMotionTarget = null;  
           // Don‘t dispatch this event to our own view, because we already  
           // saw it when intercepting; we just want to give the following  
           // event to the normal onTouchEvent().  
           return true;  
       }  

       if (isUpOrCancel) {  
           mMotionTarget = null;  
       }  

       // finally offset the event to the target‘s coordinate system and  
       // dispatch the event.  
       final float xc = scrolledXFloat - (float) target.mLeft;  
       final float yc = scrolledYFloat - (float) target.mTop;  
       ev.setLocation(xc, yc);  

       if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {  
           ev.setAction(MotionEvent.ACTION_CANCEL);  
           target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  
           mMotionTarget = null;  
       }  

       //假設沒有攔截ACTION_MOVE, ACTION_DOWN的話,直接將Touch事件派發給target  
       return target.dispatchTouchEvent(ev);  
   }  

當我們點擊button的時候,因為我們Activity中重寫的onInterceptTouchEvent返回值為super.onInterceptTouchEvent(ev);即默認的false。那麽25行的if條件!onInterceptTouchEvent(ev))為true。進入if語句裏面,遍歷全部的子View,然後運行51行的if (child.dispatchTouchEvent(ev)),上面講到了Button是可點擊的。那麽MyButton的onTouchEvent返回值為true,即dispatchTouchEvent返回值為true。消費了該事件。全部不會觸發mLinearLayout的點擊事件。log例如以下:
技術分享

那麽問題來了。為什麽將MyButton 的onTouchEvent返回值設為false,然後點擊Button就會觸發mLinearLayout的點擊事件呢?

我們來分析下:將MyButton 的onTouchEvent返回值設為false,那麽51行的if (child.dispatchTouchEvent(ev))的返回值為false,為什麽呢?

上面分析View的dispatchTouchEvent源代碼時分析過了。

返回了false,那麽看ViewGroup的源代碼。81行, final View target = mMotionTarget; 因為51行的if (child.dispatchTouchEvent(ev))返回false,所以沒有對mMotionTarget進行賦值。 mMotionTarget == null。
所以走到85行:if (target == null) //target = mMotionTarget ,所以該if條件成立。


走到93行:return super.dispatchTouchEvent(ev);

運行第9行的super.dispatchTouchEvent(ev)。viewgroup的Super是View,即運行View的dispatchTouchEvent方法。因為我們在Activity中47行設置了ontouch事件。所以先運行Activity 中 mLinearLayout.setOnTouchListener中的onTouch, onTouch返回false ,接著運行 MyLinearLayout 中的onTouchEvent。

說明:
本來因為MyLinearLayout 是繼承自LinearLayout。默認和textview一樣是沒有點擊(clickable)或長按(longclickable)的能力的。可是,我們在Activity的38行對他設置了點擊事件。mLinearLayout.setOnClickListener,所以MyLinearLayout 獲得 點擊的能力。

所以MyLinearLayout的onTouchEvent返回true,然後運行MyLinearLayout的onTouchEvent的ACTION_UP。而點擊事件就是在ACTION_UP中運行的(說明1)。

全部觸發了mLinearLayout.setOnClickListener點擊事件。log 例如以下:
技術分享

有什麽問題大家能夠交流下:

總結:
1.Touch事件是從頂層的View一直往下分發到手指按下的最裏面的View,假設這個View的onTouchEvent()返回false,即不消費Touch事件,這個Touch事件就會向上找父布局調用其父布局的onTouchEvent()處理。假設這個View返回true,表示消費了Touch事件,就不調用父布局的onTouchEvent()。


2.一個clickable或者longClickable的View會永遠消費Touch事件,無論他是enabled還是disabled的。
3.View的長按事件是在ACTION_DOWN中運行,要想運行長按事件該View必須是longClickable的,假設設置的長按事件中返回true,那麽clickable事件不會觸發。而且不能產生ACTION_MOVE。
4.View的點擊事件是在ACTION_UP中運行。想要運行點擊事件的前提是消費了ACTION_DOWN和ACTION_MOVE,而且是在沒有設置OnLongClickListener的情況下,如設置了OnLongClickListener的情況。則必須使onLongClick()返回false。
5.假設View設置了onTouchListener了,而且onTouch()方法返回true,則不運行View的onTouchEvent()方法,也表示View消費了Touch事件。返回false則繼續運行onTouchEvent()。


6.Touch事件是從最頂層的View一直分發到手指touch的最裏層的View,假設最裏層View消費了ACTION_DOWN事件(設置onTouchListener。而且onTouch()返回true 或者onTouchEvent()方法返回true)才會觸發ACTION_MOVE,ACTION_UP的發生,假設某個ViewGroup攔截了Touch事件。則Touch事件交給ViewGroup處理。

談談我對Android View事件分發的理解