編輯:關於Android編程
當我們的手指在Android屏幕上點擊或滑動時,就會觸發觸摸事件TouchEvent。在App中ViewGroup和View存在多級嵌套,在最外層的是Activity,最內層的View,介於Activity與View之間的是一些ViewGroup。本文為了簡化討論,我們假設一個Activity中只有一個ViewGroup,這個ViewGroup中只有一個View。當我們用手指觸摸到View的UI時,就會產生觸摸事件TouchEvent,總的過程如下圖所示:
首先是最外層的Activity接收到該事件,觸發Activity的dispatchTouchEvent的執行,在該方法中Activity又會調用內部ViewGroup的dispatchTouchEvent方法的執行,在ViewGroup的dispatchTouchEvent方法中又會調用最內層的View的dispatchTouchEvent方法的執行,在View的dispatchTouchEvent方法中可能會執行View的onTouchEvent方法,然後ViewGroup也有可能執行ViewGroup的onTouchEvent方法,然後Activity也有可能執行Activity的onTouchEvent方法的執行。
上圖是精簡過的主要流程圖,總共是兩條主線:
第一條主線是,從Activity -> ViewGroup -> View,從外向內依次調用dispatchTouchEvent方法,Android會依次把MotionEvent參數傳遞給該方法。dispatchTouchEvent的作用是傳遞觸摸事件,該主線體現了將觸摸事件從外向內逐級傳遞派發的過程<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqOsZGlzcGF0Y2hUb3VjaEV2ZW50ysfDv7TOtKu13bSlw/7Kwrz+tcTI67/aoaM8L3A+DQo8cD612rb+zPXW98/fysejrLTTVmlldyAtJmd0OyBWaWV3R3JvdXAgLSZndDsgQWN0aXZpdHmjrLTTxNrP8s3i0sC0zrX308NvblRvdWNoRXZlbnS3vbeoo6xBbmRyb2lku+HSwLTOsNFNb3Rpb25FdmVudLLOyv20q7XduPi4w7e9t6iho29uVG91Y2hFdmVudLXE1/fTw8rHtKbA7bSlw/7Kwrz+o6w8c3Ryb25nPrjD1vfP38zlz9bBy72rtKXD/srCvP6008Taz/LN4tbwvLa0psDttcS5/bPMPC9zdHJvbmc+oaM8L3A+DQo8cD5kaXNwYXRjaFRvdWNoRXZlbnS6zW9uVG91Y2hFdmVudLa8vdPK1dK7uPZNb3Rpb25FdmVudMDg0M21xLLOyv2jrE1vdGlvbkV2ZW50t+LXsMHLtKXD/srCvP61xMr9vt3Qxc+io6yw/MCotKXD/srCvP61xMDg0M3S1Lyw1/ix6s671sO1yKOsz+q8+7KpzsShtkFuZHJvaWTW0LXETW90aW9uRXZlbnSht6GjZGlzcGF0Y2hUb3VjaEV2ZW50us1vblRvdWNoRXZlbnS2vNPQ0ru49mJvb2xlYW7A4NDNtcS3tbvY1rWjrMjnufu3tbvYdHJ1ZaOsse3KvrWxx7C21M/z0tG+rbbUtKXD/srCvP69+NDQwcu0psDto7vI57n7t7W72GZhbHNlo6yx7cq+tbHHsLbUz/PDu9PQttS0pcP+ysK8/r340NC0psDtoaM8L3A+DQo8cD7PwsPmt9ax8LbUQWN0aXZpdHmholZpZXdHcm91cKGiVmlld7XEysK8/sXJt6KhorSmwO21xLn9s8zP6s+4y7XD96GjPC9wPg0KPGhyIC8+DQo8aDIgaWQ9"activity">Activity
dispatchTouchEvent 上述方法的關鍵是,Activity會首先通過getWindow()方法獲取當前的window對象,然後調用window的superDispatchTouchEvent方法,實際上,getWindow()返回的是一個PhoneWindow類型的實例,這樣就會調用PhoneWindow的superDispatchTouchEvent方法,其源碼如下所示: mDecor是PhoneWindow中一個DecorView類型的變量,DecorView代表了當前Window最頂級的View,可以看做是根View。由上代碼看出,後面會執行DecorView的superDispatchTouchEvent方法,其源碼如下所示: 實際上DectorView繼承自FrameLayout,所以DectorView間接繼承自ViewGroup,所以會DectorView執行其父類ViewGroup對應的dispatchTouchEvent方法。在該方法中,DectorView會找到其觸摸的子節點,實際上其子節點也是一個ViewGroup,然後再執行該ViewGroup的dispatchTouchEvent方法,這樣就實現了將觸摸事件參數MotionEvent從Activity中傳入到DecorView的子ViewGroup中了。我們會在後面探討ViewGroup中的dispatchTouchEvent方法中的執行邏輯,此處就不再過多介紹了。 以上介紹了借助superDispatchTouchEvent和dispatchTouchEvent方法將觸摸事件從Activity到ViewGroup中的傳遞過程,這兩個方法均返回一個boolean類型的參數,如果返回true,表示觸摸事件被處理了,反之表示觸摸事件沒有被處理。我們再看一下上面Activity中dispatchTouchEvent的源碼,就會發現如果PhoneWindow的superDispatchTouchEvent返回了true,那麼Activity的dispatchTouchEvent方法也就直接返回了true,表明觸摸事件被Window給處理了,所以就不會執行後面Activity的 onTouchEvent方法。只有Window沒處理觸摸事件的情況下,Activity才會調用onTouchEvent方法去處理事件。 onTouchEvent 只有當觸摸事件沒有被任何的View或ViewGroup處理過的時候,Activity才會執行自己的onTouchEvent去處理觸摸事件。一種典型的情形就是,當前觸摸點在Window范圍之外,這樣Window裡面所有的View都不會接收更不會處理該觸摸事件,這時候我們可以重寫該方法實現一些自己的邏輯處理這種情形。如果我們處理了,就返回true,否則返回false。其默認實現基本一直返回false。 dispatchTouchEvent ViewGroup中定義了一個TouchTarget類型的成員變量mFirstTouchTarget,用於保存當前ViewGroup中處理了觸摸事件的子View。 首先,dispatchTouchEvent方法會調用其自身的onInterceptTouchEvent方法,onInterceptTouchEvent是用來攔截ViewGroup將觸摸事件傳遞給其子View的,如果該方法返回true,就表示ViewGroup應該攔截觸摸事件;如果返回false,表示ViewGroup不應該攔截觸摸事件,應該將觸摸事件傳遞給子View。在dispathTouchEvent方法中還定義了一個boolean類型的handled變量,用於保存dispathTouchEvent方法的返回值,如果是true就表示觸摸事件被當前的ViewGroup處理了,反之則表示沒被處理。 然後,只有當onInterceptTouchEvent返回了false,ViewGroup才會依次遍歷其子View,其會通過調用isTransformedTouchPointInView方法判斷MotionEvent所攜帶的觸摸事件的坐標是否落在子View的范圍內,如果觸摸事件的坐標恰好落在了該子View范圍內,說明我們觸摸了當前ViewGroup內的該子View,這樣ViewGroup就會把觸摸事件的坐標以及該子View傳遞給dispatchTransformedTouchEvent方法,在該方法內會調用子View的dispatchTouchEvent方法,其返回值表示自View是否處理了觸摸事件,如果dispatchTransformedTouchEvent返回true,表示子View處理了觸摸事件,這樣ViewGroup會通過調用addTouchTarget方法將mFirstTouchTarget綁定該子View,並且變量alreadyDispatchedToNewTouchTarget也會設置為true,表示已經有子View處理了觸摸事件。一旦有子View處理了觸摸事件,ViewGroup就會通過break跳出for循環,不再對其他子View進行遍歷。 在經過了對子View的for循環之後,如果沒有任何的子View處理了觸摸事件,那麼mFirstTouchTarget就還是null,此時ViewGroup就會將null作為child參數傳入dispatchTransformedTouchEvent方法中,該方法會調用super.dispatchTouchEvent方法,由於ViewGroup繼承自View,以此處就相當於執行了View類中的dispatchTouchEvent方法,這樣就很有可能執行ViewGroup從View中繼承來的onTouchEvent方法。dispatchTransformedTouchEvent的返回值會作為局部變量handled的值。關於View類中的dispatchTouchEvent方法會在下面詳細說明。 在經過了對子View的for循環之後,如果發現某個子View對觸摸事件進行了處理,那麼alreadyDispatchedToNewTouchTarget就是true,從而會將局部變量handled設置為true,即表示只要有子View處理了觸摸事件,就表示當前的ViewGroup也處理了觸摸事件,並且這種情況下ViewGroup不會調用從View中繼承來的dispatchTouchEvent方法,從而不會觸發ViewGroup的onTouchEvent方法的執行。 onInterceptTouchEvent dispatchTransformedTouchEvent 該方法的主要目的是將MotionEvent中的x、y的坐標轉換成所傳入的child變量所指定的的View的坐標系中的坐標,transformedEvent表示了已經完成了指定坐標系轉換的MotionEvent。如果傳入的child參數是null,表示傳入的是當前的ViewGroup,此時就將直接調用super.dispatchTouchEvent(transformedEvent),這樣就讓ViewGroup調用了父類View中的dispatchTouchEvent方法;如果傳入的child參數不是null,表示傳入的當前ViewGroup的一個子View,那麼就會調用child.dispatchTouchEvent(transformedEvent),從而將觸摸事件從ViewGroup傳遞到子View中去。我們會在下面介紹View的dispatchTouchEvent的實現邏輯。 onTouchEvent dispatchTouchEvent dispatchTouchEvent是View處理觸摸事件的入口。在該方法中,View首先會查看其有沒有設置過OnTouchListener,如果設置過就調用OnTouchListener的onTouch方法,如果其返回了true,就表明觸摸事件被處理了,result就會設置為true。如果觸摸事件沒有被OnTouchListener處理,那麼就會執行View的onTouchEvent方法,如果onTouchEvent返回了true,就表示觸摸事件被View處理了,result就被設置為了true。 由上可以看出,在dispatchTouchEvent方法中是先執行OnTouchListener的onTouch方法,一旦其返回true,就不會調用View自身的onTouchEvent方法了,只有OnTouchListener沒有處理觸摸事件才會在後面執行View的onTouchEvent方法。 onTouchEvent 我們通過對上面Activity、ViewGroup、View各個層級對觸摸事件的處理過程可以發現,Android中每個層級對觸摸事件的處理都是從dispatchTouchEvent方法開始的,首先先調用下一層級的dispatchTouchEvent方法,將觸摸事件傳遞給下一層級,如果下一層級對觸摸事件進行了處理,就可認為本層級也對觸摸事件進行了處理,那麼本層級就不會對觸摸事件僅需做其他特殊處理了;如果下一層級沒有對觸摸事件進行處理,即下一層級的dispatchTouchEvent方法返回false,那麼才會調用本層級的onTouchEvent方法對觸摸事件進行處理。 我的更多博文可參見《我的Android博文整理匯總》,希望本文對大家理解Android中的觸摸事件機制有所幫助!
所有在UI上的觸摸操作生成的觸摸事件都首先會觸發Activity中dispatchTouchEvent方法的執行,其源碼如下所示:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
onTouchEvent的源碼如下所示:
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
ViewGroup
當Activity接收到觸摸事件之後,會通過DectorView調用ViewGroup的dispatchTouchEvent方法,由於該方法的源碼太長,此處就不貼源碼了,點此查看其源碼。此處主要說一下該方法中的主要邏輯。dispatchTouchEvent方法是ViewGroup對觸摸事件進行處理的入口。
之前提到過onInterceptTouchEvent用於攔截ViewGroup向子View傳遞觸摸事件,ViewGroup中的默認實現一直返回false,即表示不攔截。我們可以重寫該方法以實現我們自己的觸摸事件攔截邏輯。
點此查看源碼,其主要的邏輯代碼如下所示:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final MotionEvent transformedEvent;
......
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
ViewGroup的onTouchEvent繼承自View的onTouchEvent方法,ViewGroup並沒有重寫,我們在下面會介紹View的onTouchEvent方法的實現邏輯。
View
點此查看源碼,其源碼的主要邏輯如下所示:
public boolean dispatchTouchEvent(MotionEvent event) {
......
boolean result = false;
......
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//如果設置了OnTouchListener,那麼會在此處執行OnTouchListener的onTouch方法
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//如果OnTouchListener的onTouch方法返回true,就表示觸摸事件被處理了,result就會設置為true
result = true;
}
//如果觸摸事件沒有被OnTouchListener處理,那麼就會執行View的onTouchEvent方法
if (!result && onTouchEvent(event)) {
//如果onTouchEvent返回了true,就表示觸摸事件被View處理了,result就被設置為了true
result = true;
}
}
......
return result;
}
點此查看源碼,View.onTouchEvent()方法中,如果View注冊了CLICK或LONG_CLICK等事件監聽器,那麼就會讓注冊的事件監聽器處理觸摸事件,這樣onTouchEvent就返回true。會根據ACTION的不同,執行不同的處理,比如如果是ACTION_UP,會執行performClick()方法,該方法會觸發OnClickListener.onClick()的執行。
如果View沒有注冊任何的CLICK或LONG_CLICK等的事件監聽器,那麼onTouchEvent就返回false,表示onTouchEvent沒有對傳入的觸摸事件MotionEvent做任何處理。
總結
簡述Qt Network 模塊中提供了一些高級別的類,例如:QNetworkRequest、QNetworkReply 和 QNetworkAccessManager,使
目前本書已上傳到百度閱讀, 在百度中搜索[Anroid Studio實用指南]便可以找到本書.什麼是演示模式?顧名思義,當你想給別人演示你的代碼時就會用到這個演示模式.演
本文實例講述了Android編程之線性布局LinearLayout用法。分享給大家供大家參考,具體如下:線性布局(LinearLayout)可以讓它的子元素垂直或水平的方
如題,在Android中按鈕的點擊事件有四種寫法,如下圖。界面為四個Button+一個TextView+一個ImageViewactivity_main布局文件如下:&n