編輯:關於Android編程
當Android系統捕獲到用戶的各種輸入事件後,如何准確地傳遞給真正需要這個事件的控件呢?Android給我們提供了一整套完善的事件傳遞、處理機制,來幫助開發者完成准確的事件分配與處理。
要了解觸摸事件的攔截機制,首先要了解什麼是觸摸事件?顧名思義,觸摸事件就是捕獲觸摸屏幕後產生的事件。當點擊一個按鈕時,通常就會產生兩個或者三個事件——按鈕按下,這是事件一;如果不小心滑動一點,這就是事件二;當手抬起,這是事件三。Android為觸摸事件封裝了一個類——MotionEvent,如果重寫onTouchEvent()方法,那就會發現給方法的參數就是這樣一個MotionEvent。其實,只要是重寫觸摸相關的方法,參數一般都含有MotionEvent,可見它的重要性。
在MotionEvent裡面封裝了不少好東西,比如觸摸點的坐標,可以通過event.getX()方法和event.getRawX()方法取出坐標點;再比如獲得點擊的事件類型,可以通過不同的Action(如MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE)來進行區分,並實現不同的邏輯。
如此看來,觸摸事件還是比較簡單的,其實就是一個動作類型加坐標而已。但是我們知道,Android的View結構是樹形結構,也就是說,View可以放在ViewGroup裡面,通過不同的組合來實現不同的樣式。那麼問題來了,View放在一個ViewGroup裡面,這個ViewGroup又放在另一個ViewGroup裡面,甚至還有可能繼續嵌套,一層層地疊起來。可我們的觸摸事件就一個,到底該分給誰呢?同一個事件,子View和父ViewGroup都有可能想要進行處理。因此,這就產生了“事件攔截”這個“霸氣”的稱呼。
當然,事件攔截可以很復雜,也可以很簡單。但是初學者卻經常“卡”在這裡不知道如何繼續進行,所以我們不想通過過多的源代碼讓大家不知所措。我們通過最直觀的Log信息,讓大家先有一個大概的了解,知道事件攔截的本質,然後大家在結合源代碼學習時,就可以有方向、有目的性去理解了。
首先,請想象一下生活中非常常見的場景:假設你所在的公司,有一個總經理,級別最高;他下面有一個部長,級別次之;最低層,就是干活的你,沒有級別。現在董事會交給總經理一項任務,總經理將這項任務布置給了部長,部長又把任務安排給了你。而當你好不容易干完活了,你就把任務交給部長,部長覺得任務完成得不錯,於是就簽了他的名字交給總經理,總經理看了也覺得不錯,就業簽了名字交給董事會。這樣,一個任務就順利完成了。如果大家能非常清楚地理解這樣一個場景,那麼對於事件攔截機制,你就超過了40%的開發者了。下面,我們再來超越剩下的開發者。為了能過方便地了解整個事件的流程,我們設計了這樣一個實例,如下圖所示。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d("blankj", "ViewGroupA dispatchTouchEvent" + ev.getAction()); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d("blankj", "ViewGroupA onInterceptTouchEvent" + ev.getAction()); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("blankj", "ViewGroupA onTouchEvent" + event.getAction()); return super.onTouchEvent(event); }
而對於View來說,重寫了如下所示的兩個方法。
@Override public boolean onTouchEvent(MotionEvent event) { Log.d("blankj", "View onTouchEvent" + event.getAction()); return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d("blankj", "View dispatchTouchEvent" + event.getAction()); return super.dispatchTouchEvent(event); }
從上面的代碼中可以看到,ViewGroup級別比較高,比View多了一個方法——onInterceptTouchEvent()。這個方法看名字就能猜到是事件攔截的核心方法。我們先不修改任何返回值,只是點擊一下View,然後看Log會怎樣記錄我們的操作和程序響應。點擊View後的Log如下所示。
D/blankj: ViewGroupA dispatchTouchEvent0
D/blankj: ViewGroupA onInterceptTouchEvent0
D/blankj: ViewGroupB dispatchTouchEvent0
D/blankj: ViewGroupB onInterceptTouchEvent0
D/blankj: View dispatchTouchEvent0
D/blankj: View onTouchEvent0
D/blankj: ViewGroupB onTouchEvent0
D/blankj: ViewGroupA onTouchEvent0
可以看見,正常情況下,時間的傳遞順序是:
總經理(MyViewGroupA)→部長(MyViewGroupB)→你(View)。事件傳遞的時候,先執行dispatchTouchEvent()方法,再執行onInterceptTouchEvent()方法。
事件的處理順序是:
你(View)→部長(MyViewGroupB)→總經理(MyViewGroupA)。事件處理都是執行onTouchEvent()方法。
事件傳遞的返回值非常容易理解:true,攔截,不繼續;false,不攔截,繼續流程。
事件處理的返回值也類似:true,處理了,不用審核了;false,給上級處理。
初始情況下,返回值都是false。
這裡為了能夠方便大家理解事件攔截的過程,在事件傳遞中,我們只關心onInterceptTouchEvent(),而dispatchTouchEvent()方法雖然是事件分發的第一步,但一般情況下,我們不太會去改寫這個方法,所以暫時不管這個方法。可以把上面的整個事件過程整理成如下圖所示的一張圖。
D/blankj: ViewGroupA dispatchTouchEvent0
D/blankj: ViewGroupA onInterceptTouchEvent0
D/blankj: ViewGroupA onTouchEvent0
跟我們設想的一樣,總經理(MyViewGroupA)把所有事情都干了,沒後面人的事了。同理,我們讓部長(MyViewGroupB)也來當一次好人,即讓部長(MyViewGroupB)使用onInterceptTouchEvent()方法返回true,把事件攔截下來,Log就會使以下這樣。
D/blankj: ViewGroupA dispatchTouchEvent0
D/blankj: ViewGroupA onInterceptTouchEvent0
D/blankj: ViewGroupB dispatchTouchEvent0
D/blankj: ViewGroupB onInterceptTouchEvent0
D/blankj: ViewGroupB onTouchEvent0
D/blankj: ViewGroupA onTouchEvent0
可以看到,這次部長(MyViewGroupB)當了好人,你(MyView)就不用干活了。
那麼這兩種情況,也可以整理成類似如上圖所示的圖。
總經理(MyViewGroupA)攔截事件,如下圖所示。
D/blankj: ViewGroupA dispatchTouchEvent0
D/blankj: ViewGroupA onInterceptTouchEvent0
D/blankj: ViewGroupB dispatchTouchEvent0
D/blankj: ViewGroupB onInterceptTouchEvent0
D/blankj: View dispatchTouchEvent0
D/blankj: View onTouchEvent0
D/blankj: ViewGroupB onTouchEvent0
他們之間的關系圖如下圖所示。
一、自定義控件(一) --- 自定義屬性TextView1,定義屬性,制作attrs.xml文件;屬性值:string,color,attr,array,bool,dec
先來看下效果圖: 其中show和dismiss的時候有動畫效果。 原先試過使用PopupWindow來做,但是使用的時候不是那麼舒服,畢竟不
Android 6.0 Marshmallow首次加入了運行時權限管理,這對用戶來說,可以更好的了解、控 制 app 涉及到的權限。然而對開發者來說卻是一件比較蛋疼的事情
和其他地圖一樣,都要先去官網注冊成為開發者,然後獲取Key。下面直接上代碼。效果圖: package com.example.gaodemap;i