編輯:關於Android編程
上篇分析了View的事件分發流程,留了一個問題:如果上面的EventButton繼承TextView的話,按下抬起,會有一個現象,我可以告訴大家現象:就是只有dispatchTouchEvent ACTION_DOWN,onTouch ACTION_DOWN,onTouchEvent ACTION_DOWN這三個,你移動,或者抬起,是沒有MOVE,或者UP的。
現在說下答案:當時我們在MainActivity中用到了 EventButton.setOnTouchListener(OnTouchListener l)事件監聽,返回的是false,表示啥意思,ACTION_DOWN一次支持到switch中去,直接return了false,告訴父view,我處理不了,你不要派任務(事件傳遞)給我了,所以它只接到了ACTION_DOWN事件,接不到其他的MOVE或者UP,那問題來了,為啥繼承Button可以呢?看Button源碼,發現啥也沒干,就繼承了TextView,但用了自己的 com.android.internal.R.attr.buttonStyle,進去一看,發現兩個點,一是focusable是true的,二是android:clickable也是true的,這可是問題的答案呢,如何是isfocusable的話,就能先於父view獲取到事件。
所以它是有MOVE和UP的。有興趣的,可以把MainActivity中EventButton.setOnTouchListener(OnTouchListener l)事件監聽返回true,這個EventButton就能有MOVE和UP了,可以自己動手試試,你看到的才是答案。
前面是上篇遺留的問題,本篇開始分析ViewGroup事件分發(PS:本篇文章中源碼均是android 6.0,請知曉)
dispatchTouchEvent onInterceptTouchEvent onTouchEvent ViewGroup 事件的分發機制流程圖 案例 案例流程圖ViewGroup.java -> dispatchTouchEvent()
ViewGroup.java -> onInterceptTouchEvent()
vcu1w/fPwqO6PC9wPg0KPHA+aWYgKCFjYW5WaWV3UmVjZWl2ZVBvaW50ZXJFdmVudHMoY2hpbGQpPGJyIC8+DQrV4sDvse3KvsXQts+1scewtcRkb3duoaJQT0lOVEVSX0RPV06hokhPVkVSX01PVkXI/bj2ysK8/rXE1/ix6rXjyse38cLk1NrBy9fTv9i8/snPo6zI57n7wuTU2tfTv9i8/snPo6xpZiAoZGlzcGF0Y2hUcmFuc2Zvcm1lZFRvdWNoRXZlbnQoZXYsIGZhbHNlLCBjaGlsZCwgaWRCaXRzVG9Bc3NpZ24pKc2ouf1kaXNwYXRjaFRyYW5zZm9ybWVkVG91Y2hFdmVudLSrtd3Kwrz+o6y9u9PJ19O/2Lz+xdC2z8rHt/G0q7Xdu/LX1Ly6z/u30bSmwO2ho8jnuftkaXNwYXRjaFRyYW5zZm9ybWVkVG91Y2hFdmVudLe1u9h0cnVlo6yx7cq+19O/2Lz+0tHP+7fRtKbA7aOssqLM7bzTtMvX07/YvP5WaWV3tb20pcP+wbSx7aOssqK3xdbDwbSx7c23o6yyor3hyvix6cD619O/2Lz+oaNuZXdUb3VjaFRhcmdldCA9IGFkZFRvdWNoVGFyZ2V0KGNoaWxkLCBpZEJpdHNUb0Fzc2lnbik7ZmFsc2Wx7cq+zrS0psDtoaM8L3A+DQo8cD5vbkludGVyY2VwdFRvdWNoRXZlbnSx7cq+yse38cC5vdi1scewysK8/qOst7W72HRydWWx7cq+wLm92KOsyOe5+8C5vdjBy8rCvP6jrMTHw7S9q7K7u+G31reiuPjX01ZpZXeho8jno7pWaWV3R3JvdXDAub3YwcvV4rj2ysK8/qOsxMfDtMv509DKwrz+trzTybjDVmlld0dyb3VwtKbA7aOsy/zE2rK/tcTX01ZpZXe9q7K7u+G78bXDysK8/rXEtKu13aGjVmlld0dyb3VwysfErMjPsrvAub3YysK8/rXEo6zXotLio7pWaWV3ysfDu9PQ1eK49re9t6i1xKOs0rK8tMrHy7WjrLzMs9DX1FZpZXe1xNK7uPbX01ZpZXeyu8Tc1tjQtLjDt723qKOs0rLO3tDowLm92MrCvP6jrNLyzqrL/M/Cw+bDu9PQVmlld8HLo6zL/NKqw7S0psDtysK8/tKqw7Syu7SmwO3Kwrz+o6zL+dLU1+6117LjtcTX01ZpZXeyu8TcwLm92MrCvP6hozwvcD4NCjxwPtfTVmlld8/7usTBy0FDVElPTl9ET1dOysK8/qOsRE9XTixNT1ZFLFVQtrzKx9K7uPa72LrPtcTKwrz+o6w8YnIgLz4NCkRPV04gLSZndDsgb25JbnRlcmNlcHRUb3VjaEV2ZW50IC0mZ3Q7IHRydWUgvs2yu8/yz8K0q8rCvP6jrMfSb25JbnRlcmNlcHRUb3VjaEV2ZW50srvU2bX308M8YnIgLz4NCkRPV04gLSZndDsgb25JbnRlcmNlcHRUb3VjaEV2ZW50IC0mZ3Q7IGZhbHNlILSrz8LIpTxiciAvPg0KTU9WRSAtJmd0OyBvbkludGVyY2VwdFRvdWNoRXZlbnQgLSZndDsgdHJ1ZSCyu7SrPGJyIC8+DQrDv7j2u9i6z7a80qq+rcD6Vmlld0dyb3Vw1tC1xG9uSW50ZXJjZXB0VG91Y2hFdmVudLXExdC2z8rHt/HSqsC5vdi908/CwLS1xNXiuPbX01ZpZXe1xMrCvP6jqLWxyLvXotLix7DM4cz1vP7Kx8O/tM62vMrHt7W72GZhbHNlsrvAub3YyrGjrLTyuPayu8ehtbG1xLHI0/ejutXiuPa+zbrDz/HE48Lywfm6z7LK0rvR+aOs0qrKx8Tj1tDBy7TzvbGjrMTj1sHJ2dK7ts7KsbzkxNqjrMTjtryyu7vh1NnC8sHLo6zI57n7w7vW0KOsxMe+zcO/tM62vNKqwvKjqaO7u/LV36OsyOe5+7Xa0ru0zkRPV07Kwrz+uvOjrNPJVmlld0dyb3Vwsb7J7c/7t9G19MHLo6zEx21GaXJzdFRvdWNoVGFyZ2V0vs3Oqm51bGyjrM6qv9W1xLuwo6zLtcP3Vmlld0dyb3VwsrvP69XiuPbKwrz+tKu1vcv8tcTX03ZpZXfW0Milo6y0y7rzTU9WRbvy1d9VUMrCvP62vL2r1NpWaWV3R3JvdXDV4rLj19S8urSmwO2jrLK7u+G0q7W919N2aWV31tDIpaGjPC9wPg0KtPK49rHI0/ejqLbU1dXKwrz+tKu13bv61sbAtKOpo6y5+rzSo6jW0NHro6m3osHL0ru33bXnsai1vcqhwO/D5ijP4LWx09rSu7TOysK8/kRPV04sTU9WRSxVUMbk0ru2vLrNKaOsudjT2sa2wKeyudb6xLO12Mf4tcSjrMjnufvHrrHIvc+24KOsyqHA78PmvvW1w9Xi083LrrXDwMzSu8DMo6jAub3Yb25JbnRlcmNlcHRUb3VjaEV2ZW50ILe1u9h0cnVlo6mjrNPaysew0dXiuPbKwrz+wLm92M/Co6zX1Ly6z/u30bX0wcujrNaxvdNyZXR1cm4gdHJ1ZaOsuObL38nPw+ajrNXiuPbKwrz+z/u6xM3qsc+jrLe01f3Jz8Pm0rKyu9aqtcCjqNTdx9LV4sO0y7WwyaOpo6zPwtK7tM6jrLn6vNKjqNbQ0eujqdPWt6LBy9K7t92157Gotb3KocDvw+ajrKOsudjT2sa2wKeyudb6xLO12Mf4tcSjrNXitM7HrrHIvc/J2aOsyqHA78PmvvW1w8O7ybbAzLXEo6iyu8C5vdggwLm92G9uSW50ZXJjZXB0VG91Y2hFdmVudCC3tbvYZmFsc2WjqaOsvs2w0dXiuPbP+8+i1K234rK7tq+3os/ywcu12Le91f64rqOstdi3vdX+uK7P+7fR1eK49srCvP6jrMi7uvO3tbvYdHJ1ZaGjDQo8aDIgaWQ9"dispatchtransformedtouchevent">dispatchTransformedTouchEvent
ViewGroup.java -> dispatchTransformedTouchEvent()
當傳遞進來的子View不為null時,就會調用子View的dispatchTouchEvent(event)方法進行事件分發,事件交給子View處理,也即是說,子Viwe符合父view不攔截條件時,事件就會在這裡傳遞給了子View來處理,完成了ViewGroup到子View的事件傳遞,上面多次調用child.dispatchTouchEvent,這其實就回到上一篇文章中view.dispatchTouchEvent中,而昨天已經分析,view.dispatchTouchEvent接下來會調用view.onTouchEvent返回處理結果,當view事件處理完畢,就會返回一個handled給ViewGroup,就是告訴ViewGroup,是否它消費了這個事件,假如子View的onTouchEvent()返回true,那麼就是消耗了事件。
ViewGroup.java -> addTouchTarget()
要是覺得細節太瑣碎,總結成下面一張圖,看清上面的過程:
案例:
MainActivity
自定義的RelativeLayout
自定義button
布局文件
運行studio,點擊按鈕,輸出log結果:
從log中可以看出:執行過程是從CustomRelativeLayout的dispatchTouchEvent ->CustomRelativeLayout的onInterceptTouchEvent -> EventButton的dispatchTouchEvent ->EventButton的onTouchEvent
在View上觸發事件,最先捕獲到事件的為View所在的ViewGroup,然後才會到View自身~
同樣這個案例,總結成下面一張圖,看清上面的過程:
1、一個事件傳遞從ACTION_DOWN開始,中間有若一個或者多個ACTION_MOVE,最後以ACTION_UP結束。
2、ViewGroup默認不攔截任何事件,所以事件能正常分發到子View處(如果子View符合條件的話),如果沒有合適的子View或者子View不消耗ACTION_DOWN事件,那麼接著事件會交由ViewGroup處理,並且同一事件序列之後的事件不會再分發給子View了。如果ViewGroup的onTouchEvent也返回false,即ViewGroup也不消耗事件的話,那麼最後事件會交由Activity處理。即:逐層分發事件下去,如果都沒有處理事件的View,那麼事件會逐層向上返回。
3、如果某一個View攔截了事件,那麼同一個事件序列的其他所有事件都會交由這個View處理,此時不再調用View(ViewGroup)的onIntercept()方法去詢問是否要攔截了。
4、子View可以通過調用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup對其MOVE或者UP事件進行攔截
5、ViewGroup自身並沒有onTouchEvent方法,在dispatchTouchEvent時,會調用dispatchTransformedTouchEvent分發到子view中去,觸發ouTouchEvent,但是我們自定義View,繼承ViewGroup時,是可以重寫ViewGroup的onTouchEvent方法,這是為什麼呢?因為ViewGroup繼承View,而View是有onTouchEvent方法,所以自定義View繼承ViewGroup時,就間接用到了父類的父類中的方法。這樣的方處是,對於ViewGroup來說,只是負責事件分發,如果有人繼承它,說明想自己控制一些事件發分流程,那麼自然相關方法也都可以重寫。
新手弄第三方類庫也許會很納悶,有時弄幾天都不行。那就讓我帶大家10分鐘做一個簡單的短信驗證吧!1.首先上Mob官網注冊賬號:http://www.mob.com/2.下載
Ionic是一款流行的移動端開發框架,但是剛入門的同學會發現,Ionic在iOS和Android的底部tabs顯示不一樣。在安卓情況下底部tabs會浮上去。如下圖展示:網
OKHttp是一款高效的HTTP客戶端,支持連接同一地址的鏈接共享同一個socket,通過連接池來減小響應延遲,還有透明的GZIP壓縮,請求緩存等優勢。(GitHub頁:
郁悶了半天,今天發現一點擊手機 menu 鍵應用就崩潰了,記得之前都是好好的,調試了半天代碼還是搞不定,於是網上google了一番,發現僅國外有一兩篇文章有提到類