Android觸摸事件傳遞

前言:本篇博文主要分析關(guān)于onTouchEvent,dispatchTouchEvent和onInterceptTouchEvent這三個(gè)方法的作用和它們之間的關(guān)系,通過(guò)流程圖來(lái)解釋觸摸事件(MotionEvent)是怎么在三個(gè)方法中傳遞的;

為了了解三個(gè)方法之間的關(guān)系,我們通過(guò)一個(gè)demo來(lái)分析它們之間的關(guān)系,demo主要內(nèi)容為在一個(gè)activity上設(shè)置兩個(gè)布局控件,一個(gè)為L(zhǎng)inearLayout,一個(gè)是TextView,兩個(gè)控件都自定義重寫(xiě)了它們的onTouchEvent、dispatchTouchEvent和onInterceptTouchEvent方法,并用logcat打印出各個(gè)方法相應(yīng)的log標(biāo)記,通過(guò)觸摸控件后log的打印順序就可知道他們的調(diào)用順序了,通過(guò)調(diào)用順序去分析事件的傳遞流程,如下圖所示為activity的視圖


其中紅色區(qū)域?yàn)門(mén)extview的視圖,底下綠色區(qū)域?yàn)長(zhǎng)inearLayout的視圖,最底下藍(lán)色區(qū)域?yàn)閍ctivity的視圖;下面貼上代碼:

public class GestureDemo extends Activity {  

    public final static String TAG = "GestureDemo";  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);   
    }  
      
    @Override  
    public boolean onTouchEvent(MotionEvent event) {  
          
        Log.i(TAG,"Activity onTouchEvent. action="+getAction(event));  
        return super.onTouchEvent(event);  
    }  
      
    @Override  
    public boolean dispatchTouchEvent(MotionEvent ev) {  
          
        Log.i(TAG,"Activity dispatchTouchEvent. action="+getAction(ev));  
        return super.dispatchTouchEvent(ev);  
    }   
}  
public class MyLinearLayout extends LinearLayout {  
  
    public MyLinearLayout(Context context) {  
        super(context);  
    }  
      
    public MyLinearLayout(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
    }  
      
    public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
        super(context,attrs,defStyleAttr);  
    }  
      
    @Override  
    public boolean dispatchTouchEvent(MotionEvent ev) {  
  
        Log.i(GestureDemo.TAG,"MyLinearLayout dispatchTouchEvent. action="+GestureDemo.getAction(ev));  
        return super.dispatchTouchEvent(ev);  
    }  
      
    @Override  
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
        Log.i(GestureDemo.TAG,"MyLinearLayout onInterceptTouchEvent. action="+GestureDemo.getAction(ev));  
        return super.onInterceptTouchEvent(ev);  
    }  
      
    @Override  
    public boolean onTouchEvent(MotionEvent event) {  
        Log.i(GestureDemo.TAG,"MyLinearLayout onTouchEvent. action="+GestureDemo.getAction(event));  
        return super.onTouchEvent(event);  
    }  
}
public class MyTextView extends TextView {  
  
    public MyTextView(Context context) {  
        super(context);  
    }  
  
    public MyTextView(Context context, AttributeSet attrs){  
        super(context,attrs);  
    }  
      
    MyTextView(Context context, AttributeSet attrs, int defStyleAttr){  
        super(context,attrs,defStyleAttr);  
    }  
      
    @Override  
    public boolean dispatchTouchEvent(MotionEvent event) {  
        Log.i(GestureDemo.TAG,"MyTextView dispatchTouchEvent. action="+GestureDemo.getAction(event));  
        return super.dispatchTouchEvent(event);  
    }  
      
    @Override  
    public boolean onTouchEvent(MotionEvent event) {  
        Log.i(GestureDemo.TAG,"MyTextView onTouchEvent. action="+GestureDemo.getAction(event));  
        return super.onTouchEvent(event);  
    }  
} 

注: 每一次屏幕觸摸后的手勢(shì)事件都是連續(xù)地由一個(gè)down事件、若干move事件和一個(gè)up事件組成,每次都必須是down事件先發(fā)生,最后一個(gè)是up事件;

  • 觸摸事件:每一個(gè)down事件、move事件或up事件都為一個(gè)獨(dú)立的觸摸事件;
  • 手勢(shì)事件:由一系列連續(xù)的觸摸事件組成的一個(gè)完整的手勢(shì),比如單擊手勢(shì)事件為down-up, 滑動(dòng)事件為down-move…move-up;

下面通過(guò)逐個(gè)改變各個(gè)方法的返回值,分析它們之間的傳遞圖;

一、 關(guān)于onTouchEvent事件

1. 所有方法都返回父類的方法,即MotionEvent沒(méi)有被子控件消耗掉,最終只有在activity的onTouchEvent消失掉(系統(tǒng)默認(rèn)傳遞流程);
2. Activity的onTouchEvent方法返回true,其事件傳遞流程和上一個(gè)相同
3. MyLinearLayout的onTouchEvent返回true
4. MyTextView的onTouchEvent返回true;

總結(jié):以上四張圖中,只改動(dòng)onTouchEvent的返回值,其他方法都調(diào)用父類方法,也就是傳遞流程都為系統(tǒng)默認(rèn)的流程,沒(méi)有人為干預(yù),圖1即是默認(rèn)的系統(tǒng)流程,而圖2,3,4通過(guò)改變onTouchEvent的返回值為true來(lái)終止事件的繼續(xù)傳遞;從圖中可知,當(dāng)屏幕觸摸發(fā)生時(shí),down事件每次都是由系統(tǒng)先調(diào)用最底層的activity的dispatchTouchEvent事件,然后繼續(xù)往上傳遞到最頂層控件,然后由onTouchEvent往下傳遞,按照默認(rèn)傳遞順序,如果哪個(gè)控件返回true,事件就在那個(gè)控件停止,即被這個(gè)控件消耗掉,如:activity和MyLinearLayout的onTouchEvent都返回true,那么事件只傳遞到MyLinearLayout的onTouchEvent,因?yàn)槭录冉?jīng)過(guò)MyLinearLayout的onTouchEvent,先被它截住了;而后續(xù)的move、up事件則會(huì)根據(jù)上個(gè)down事件最終在哪個(gè)控件被消耗掉,然后按照默認(rèn)系統(tǒng)流程傳遞到該控件的dispatchTouchEvent后直接傳遞給自己的onTouchEvent,不會(huì)再往上層控件傳遞;

二、關(guān)于dispatchTouchEvent

1. MyLinearLayout的dispatchTouchEvent返回true
2. MyTextView的dispatchTouchEvent返回true
3. MyLinearLayout的dispatchTouchEvent返回false
4. MyTextView的dispatchTouchEvent返回false

總結(jié):dispatchTouchEvent從方法名大約就可以知道其功能是分發(fā)觸摸事件,該方法會(huì)根據(jù)它的返回值來(lái)決定事件要往哪個(gè)方向傳遞:

(1)若返回父類方法,則交由系統(tǒng)來(lái)決定,即為系統(tǒng)默認(rèn)方向繼續(xù)傳遞給上層控件的方法;
(2)若返回true,則不再繼續(xù)傳遞,傳遞到這個(gè)方法為止,即這個(gè)方法消耗了事件;
(3)若返回false,事件傳遞到該方法后,事件退回到底下的父控件,繼續(xù)往系統(tǒng)默認(rèn)方向傳遞;如圖8所示,MyTextView的dispatchTouchEvent返回false,事件退回到MyLinearLayout的onInterceptTouchEvent,繼續(xù)往MyLinearLayout的onTouchEvent傳遞,即相當(dāng)于以MyLinearLayout為最頂層控件;

三、關(guān)于onInterceptTouchEvent

1. MyLinearLayout的onInterceptTouchEvent返回true

總結(jié):該方法只有ViewGroup的控件才會(huì)有,其父類的方法直接返回false,所以返回false就為圖1中按默認(rèn)方向傳遞了;該方法的作用就是攔截觸摸事件,如果返回true,就是在當(dāng)前控件攔截住觸摸事件,傳遞給自己的onTouchEvent方法,再繼續(xù)往底下控件傳遞給onTouchEvent,如果返回false或父類方法,就傳遞給它上面控件的dispatchTouchEvent;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容