老生常談android中的事件傳遞和處理機(jī)制
一直以來(lái),都被android中的事件傳遞和處理機(jī)制深深的困擾!今天特意來(lái)好好的探討一下?,F(xiàn)在的感覺(jué)是,只要你理解到位,其實(shí)事件的
傳遞和處理機(jī)制并沒(méi)有想象中的那么難??傊?,不要自己打擊自己,要相信自己能掌握這塊知識(shí)。好了,下面是我今天的收獲,希望也
能對(duì)你有一點(diǎn)幫助。
一、擬人化來(lái)理解android中的事件機(jī)制
其實(shí)android中的事件傳遞與處理機(jī)制跟我們生活中的事件處理是一樣的。這里有一個(gè)生活中的例子,很能說(shuō)明這個(gè)問(wèn)題。闡述如下:
你是一個(gè)公司的員工,你的上頭有一個(gè)主管,主管上頭呢還有一個(gè)經(jīng)理。為了簡(jiǎn)單,你們這個(gè)團(tuán)隊(duì)就有這三個(gè)人。那么如果上頭安排一件事下來(lái)要處理,流程是怎樣的呢?顯然應(yīng)該是由你的經(jīng)理將這件事安排給你的主管來(lái)處理,你的主管再將這件事安排給你來(lái)處理。等你把這件事辦好了,你就應(yīng)該給你的主管報(bào)告,再由你的主管來(lái)向你的經(jīng)理報(bào)告。顯然,你的主管和經(jīng)理也有處理這件事的權(quán)限,如果他們覺(jué)得事情很復(fù)雜,你辦不了,或者他們比較照顧下級(jí),可能就自己把這件事給辦了,這個(gè)時(shí)候這件事就不會(huì)再傳遞給下一級(jí)來(lái)處理了。這個(gè)事件處理的過(guò)程,是不是太容易理解了!
其實(shí)android中的事件處理流程就是跟生活中的事件處理是一樣的。比如你在ViewGroupA中嵌套了一個(gè)VewiGroupB,然后又在ViewGroupB
中嵌套了一個(gè)MyView。那么一個(gè)觸摸事件傳遞過(guò)來(lái),會(huì)發(fā)生什么情況呢?類比上面的公司員工的處理事件,顯然會(huì)發(fā)生下面的過(guò)程:
觸摸事件傳遞過(guò)來(lái)后,ViewGroupA一看自己里面還有一個(gè)員工可以利用,就是ViewGroupB,那不用白不用,就會(huì)把這個(gè)事件傳遞給ViewGroupB,告訴他,你給我把這個(gè)事件處理了!
ViewGroupB呢一看,我不怕,我里面也有一個(gè)員工就是MyView,它得給我干活,于是又會(huì)把這個(gè)事件傳遞給MyView,讓它來(lái)處理。MyView一看,沒(méi)辦法啊,我手底下沒(méi)有員工了,那怎么辦,我只能自己處理了(前提是它有處理這個(gè)事件的能力),所以就把這個(gè)觸摸事件給處理了。處理完成后呢?MyView就是給ViewGroupB報(bào)告,我已經(jīng)把事情辦好了,你來(lái)審核一下
,看看辦理的咋樣。ViewGroupB一審核,覺(jué)得不錯(cuò),就再將結(jié)果呈現(xiàn)給ViewGroupA。ViewGroupA再審核,通過(guò)了才算通過(guò)。在這個(gè)過(guò)程中,也可能出現(xiàn)幾種情況:
(1)MyView說(shuō),完蛋了,這事我的能力辦不好啊,于是就向VeiwGroupB報(bào)告,我沒(méi)有處理,請(qǐng)你來(lái)處理,你是我上司,能力比我強(qiáng)。于是ViewGroupB就會(huì)來(lái)幫忙處理。當(dāng)然了,
如果ViewGroupB也沒(méi)能力處理,那就只能反饋給VeiwGroupA,讓它來(lái)消化這個(gè)事件。
(2)也可能MyView處理非常完美,向ViewGroupB一報(bào)告,ViewGroupB一開(kāi)心就說(shuō)不用再交給ViewGroupA審核了,我擔(dān)保通過(guò),于是事件到此直接終止。
上面用很形象的話來(lái)講adnroid中的事件傳遞和處理機(jī)制講解了一下。android用下面的幾個(gè)方法將上面的過(guò)程完美封裝了:
在ViewGroup中,有下面三個(gè)方法: (1)dispatchTouchEvent 該方法用來(lái)分發(fā)事件,一般不會(huì)重寫這個(gè)方法 (2)onInterceptTouchEvent 用來(lái)攔截事件 (3)onTouchEvent 用來(lái)處理事件,這個(gè)方法應(yīng)該大家很常見(jiàn)了吧 而View中,只有兩個(gè)方法,即:(1)dispatchTouchEvent 該方法用來(lái)分發(fā)事件,一般不會(huì)重寫這個(gè)方法 (2)onTouchEvent 用來(lái)處理事件,這個(gè)方法應(yīng)該大家很常見(jiàn)了吧
那么我們就來(lái)寫一個(gè)實(shí)際的代碼,來(lái)驗(yàn)證一下這些方法都對(duì)應(yīng)上面的哪些過(guò)程。這樣子就會(huì)對(duì)這個(gè)些方法有更透徹的理解。
二、根據(jù)實(shí)戰(zhàn)代碼來(lái)分析各個(gè)方法
下面我們就來(lái)把上面提到的ViewGroupA,ViewGroupB,還有MyView給編寫出來(lái)。
新建一個(gè)項(xiàng)目,先來(lái)寫ViewGruopA,代碼如下:
package com.example.testmotionevent;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class ViewGroupA extends LinearLayout{
public ViewGroupA(Context context) {
super(context);
}
public ViewGroupA(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupA(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("付勇焜----->","ViewGroupA dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("付勇焜----->","ViewGroupA onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("付勇焜----->","ViewGroupA onTouchEvent");
return super.onTouchEvent(event);
}
}
代碼很簡(jiǎn)單,就不用我解釋了吧,無(wú)非就是重寫上面提到的那幾個(gè)方法,然后打印相關(guān)的標(biāo)記,來(lái)觀察事件的處理機(jī)制。
同理,編寫ViewGroupB,如下:
package com.example.testmotionevent;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class ViewGroupB extends LinearLayout{
public ViewGroupB(Context context) {
super(context);
}
public ViewGroupB(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupB(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("付勇焜----->","ViewGroupB dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("付勇焜----->","ViewGroupB onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("付勇焜----->","ViewGroupB onTouchEvent");
return super.onTouchEvent(event);
}
}
然后再編寫MyView,如下:
package com.example.testmotionevent;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class MyView extends View{
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d("付勇焜---->","MyView dispatchTouchEvent ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("付勇焜---->","MyView onTouchEvent ");
return super.onTouchEvent(event);
}
}
好了,現(xiàn)在就將它們嵌套在一起,修改activity_main.xml,如下:
<LinearLayout xmlns: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.example.testmotionevent.ViewGroupA
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#ff0033">
<com.example.testmotionevent.ViewGroupB
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:background="#336699">
<com.example.testmotionevent.MyView
android:layout_width="100dp"
android:layout_height="100dp"
android:clickable="true"
android:background="#ffff00"/>
</com.example.testmotionevent.ViewGroupB>
</com.example.testmotionevent.ViewGroupA>
</LinearLayout>
嵌套完成,運(yùn)行程序,是什么樣子的呢?如下圖:

紅色的就是ViewGroupA,藍(lán)色的就是ViewGroupB,黃色就是MyView。現(xiàn)在來(lái)點(diǎn)擊中間黃色的MyView,觀察下打印結(jié)果,如下:

從打印的結(jié)果,我們可以很清楚到看到事件的流程:
首先ViewGroupA得到了事件,由它的dispatchTouchEvent方法來(lái)分發(fā)事件,由于它的onInterceptTouchEvent方法沒(méi)有做出攔截,因此事件傳遞給了ViewGroupB,
而同樣由于ViewGroupB的onInterceptTouchEvent方法在它的dispatchTouchEvent方法分發(fā)事件時(shí)沒(méi)有做出攔截,故而事件最終被傳遞給MyView,MyView就來(lái)處理這個(gè)事件了。
下面我們來(lái)做實(shí)驗(yàn)吧,我們讓MyView沒(méi)有處理這個(gè)事件,會(huì)是上面我們所說(shuō)的由ViewGrouPB來(lái)處理嗎?修改MyView的onTouchEvent事件,如下:
public boolean onTouchEvent(MotionEvent event) {
Log.d("付勇焜---->","MyView onTouchEvent ");
return false;
// return super.onTouchEvent(event);
}
我們r(jià)eturn了false,表示MyView沒(méi)有成功處理這個(gè)事件?,F(xiàn)在來(lái)再運(yùn)行下程序,打印結(jié)果如下:

由于MyView的onTouchEvent返回false,因此事件就交給了它的上級(jí)ViewGroupB來(lái)處理,于是ViewGroupB的onTouchEvent就來(lái)消化這個(gè)事件了。所以
從打印的結(jié)果來(lái)看正是這個(gè)情況(即紅色線條標(biāo)注的部分)。但是由于ViewGroupB的onTouchEvent也沒(méi)有成功處理這個(gè)事件所以又傳遞給ViewGroupA的
onToucEvent來(lái)處理這個(gè)事件(即紅色線最下面還有ViewGroupA的標(biāo)志)。不管ViewGroupA能不能成功處理,我們的程序中它是終極boss,不會(huì)再由其他對(duì)象
來(lái)處理該事件了。
再來(lái)在上一步的基礎(chǔ)上繼續(xù)做實(shí)驗(yàn)。當(dāng)MyView沒(méi)有成功處理事件,傳遞給ViewGroupB來(lái)處理時(shí),當(dāng)ViewGroupB處理完,我們強(qiáng)制告知程序,ViewGroupB
已經(jīng)成功處理該事件了,看看會(huì)出現(xiàn)什么情況!修改ViewGroupB的onTouchEvent代碼,如下:
public boolean onTouchEvent(MotionEvent event) {
Log.d("付勇焜----->","ViewGroupB onTouchEvent");
return true; //表示事件已經(jīng)成功處理
// return super.onTouchEvent(event);
}
再來(lái)運(yùn)行程序,觀察打印結(jié)果如下:

觀察紅線部分,此時(shí)只剩下ViewGroupB的onTouchEvent了。因?yàn)閂iewGroupB已經(jīng)成功處理這事件了,那就不必再勞煩ViewGroupA來(lái)吃處理了。
好了,現(xiàn)在我們?cè)谏弦粋€(gè)實(shí)驗(yàn)的基礎(chǔ)上再來(lái)實(shí)驗(yàn)。比如,當(dāng)事件傳遞到ViewGroupB的時(shí)候,ViewGroupB比較好心,心想干脆我來(lái)處理這個(gè)事情吧,就不必再讓MyView
加班了,于是他對(duì)這個(gè)事件進(jìn)行了攔截!修改ViewGroupB的onInterceptTouchEvent代碼,如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("付勇焜----->","ViewGroupB onInterceptTouchEvent");
return true;
// return super.onInterceptTouchEvent(ev);
}
再次運(yùn)行程序,觀察打印結(jié)果,如下:

我們發(fā)現(xiàn)事件傳遞到ViewGroupB的地方直接終止了,然后就是ViewGroupB的onTouchEvent事件來(lái)處理了,當(dāng)然了因?yàn)橹拔覀儚?qiáng)制修改ViewGroupB的onTouchEvent
為處理成功,因此也不會(huì)再返回給ViewGroupA的onTouchEvent來(lái)處理了。此時(shí)MyView壓根就不知道有個(gè)觸摸事件在它的上層傳遞呢!所以在打印結(jié)果中,我們連MyView的影子
都見(jiàn)不到!
好了,我就帶大家做這幾個(gè)實(shí)驗(yàn)吧。已經(jīng)足夠說(shuō)明問(wèn)題了。下面我們就來(lái)做一下總概述吧。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
從上面的實(shí)驗(yàn)我們可以很清晰的看到一個(gè)事件處理的流程,在正常情況下,是如下圖的這樣子的一個(gè)流程:

也就是說(shuō),一個(gè)事件是必須要先經(jīng)過(guò)傳遞流程才會(huì)再經(jīng)過(guò)處理流程。這個(gè)先后順序一定要明白。如果你無(wú)法理解,請(qǐng)?jiān)傧胍幌肷厦鏀M人化的公司員工處理事件的流程吧。
其中,在紅色線和藍(lán)色線代表傳遞的流程中,我們都可以進(jìn)行所謂的攔截事件。說(shuō)明如下:
對(duì)于事件的攔截,我們主要重寫就是OnInterceptTouchEvent和onTouchEvent方法。
兩句就可以總結(jié):
(1)對(duì)于事件的傳遞,返回結(jié)果為true,表示攔截,不再往下傳遞,為false,不攔截,繼續(xù)往下傳遞。主要針對(duì)的就是OnInterceptTouchEvent方法。
(2)對(duì)于事件的處理,返回結(jié)果為true,表示攔截,不再往上傳遞(即我處理的很完美,不需要你再來(lái)審核我?。?,返回結(jié)果為false(沒(méi)有成功處理事件),繼續(xù)向上傳遞。 針對(duì)就是onTouchEvent方法。
因此我們就可以通過(guò)控制OnInterceptTouchEvent和onTouchEvent方法的返回值來(lái)控制整個(gè)事件的傳遞流程和處理流程?。〉酱?,你是不是對(duì)andorid中的事件的整個(gè)處理機(jī)制很
明白了呢?以后再出現(xiàn)什么問(wèn)題,是不是就可以順藤摸瓜,找到問(wèn)題所在了!
三、總結(jié)
如果你嫌上面的解釋太啰嗦了。那么就只看下面的這個(gè)總結(jié)就可以了,一下就找到關(guān)鍵的知識(shí)點(diǎn)!如下:
(1)正常情況下,android中的事件是必須要經(jīng)過(guò)傳遞流程然后再經(jīng)過(guò)處理流程的。要記住這個(gè)先后的順序。
(2)在傳遞流程和處理流程中,你都可以修改方法的返回值,來(lái)對(duì)流程做控制。
如下:
對(duì)于事件的攔截,我們主要重寫就是OnInterceptTouchEvent和onTouchEvent方法。兩句就可以總結(jié):
事件的傳遞,返回結(jié)果為true,表示攔截,不再往下傳遞,為false,不攔截,繼續(xù)往下傳遞。主要針對(duì)的就是OnInterceptTouchEvent方法。
事件的處理,返回結(jié)果為true,表示攔截,不再往上傳遞(即我處理的很完美,不需要你再來(lái)審核我?。?,返回結(jié)果為false(沒(méi)有成功處理事件),繼續(xù)向上傳遞。
針對(duì)就是onTouchEvent方法。
(3)如果流程你還理解,就好好想一想那個(gè)公司員工的擬人化解釋吧!實(shí)際上android的事件處理機(jī)制原理就是這樣子的!
以上這篇老生常談android中的事件傳遞和處理機(jī)制就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android 中自定義Dialog樣式的Activity點(diǎn)擊空白處隱藏軟鍵盤功能(dialog不消失)
項(xiàng)目中需要開(kāi)發(fā)帶有EditText的Dialog顯示,要求在編輯完EditText時(shí),點(diǎn)擊Dilog的空白處隱藏軟鍵盤。但是Dialog不會(huì)消失。下面通過(guò)實(shí)例代碼給大家分享實(shí)現(xiàn)方法,需要的的朋友參考下吧2017-04-04
Android基礎(chǔ)教程數(shù)據(jù)存儲(chǔ)之文件存儲(chǔ)
這篇文章主要介紹了Android基礎(chǔ)教程數(shù)據(jù)存儲(chǔ)之文件存儲(chǔ)的相關(guān)資料,數(shù)據(jù)存儲(chǔ)是Android開(kāi)發(fā)的重要的知識(shí),這里提供了實(shí)例,需要的朋友可以參考下2017-07-07
Android 實(shí)現(xiàn)全屏顯示的幾種方法整理
這篇文章主要介紹了Android 實(shí)現(xiàn)全屏顯示的幾種方法整理的相關(guān)資料,需要的朋友可以參考下2017-03-03
基于Android 錯(cuò)誤信息捕獲發(fā)送至服務(wù)器的詳解
本篇文章是對(duì)Android中錯(cuò)誤信息捕獲發(fā)送服務(wù)器進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Android?Room數(shù)據(jù)庫(kù)加密詳解
這篇文章主要為大家詳細(xì)介紹了Android?Room數(shù)據(jù)庫(kù)加密,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
android將搜索引擎設(shè)置為中國(guó)雅虎無(wú)法搜索問(wèn)題解決方法
android 進(jìn)入搜索,將搜索引擎設(shè)置為中國(guó)雅虎,無(wú)法搜索到相關(guān)網(wǎng)絡(luò)結(jié)果,該問(wèn)題是由于yahoo的搜索接口改變導(dǎo)致,具體解決方法如下,感興趣的朋友可以參考下哈2013-06-06
Android實(shí)現(xiàn)實(shí)時(shí)通信示例
本篇文章主要介紹了Android實(shí)時(shí)通信示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03
Android實(shí)現(xiàn)藍(lán)牙客戶端與服務(wù)器端通信示例
這篇文章主要介紹了Android實(shí)現(xiàn)藍(lán)牙客戶端與服務(wù)器端通信示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-01-01
Android獲取SD卡路徑及SDCard內(nèi)存的方法
這篇文章主要介紹了Android獲取SD卡路徑及SDCard內(nèi)存的方法,較為詳細(xì)的分析了Android針對(duì)SD卡操作所涉及的類及其具體函數(shù)功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-02-02

