View事件體系

View參數概念

1.位置參數

由頂點決定袁滥,對應于View的四個屬性: top(左上角縱坐標)贵白、left(左上角橫坐標)bottom(右下角縱坐標)睬塌、right(右下角橫坐標)齿税,相對于View的父容器屋剑,view的寬高width = right – left迄薄,height= bottom – top

獲裙潦荨:getLeft(),getRight(), getTop(), getBottom();

Android3.0開始新增x, y, translationX, translationY

x,y表示view相對于父容器的左上角坐標,translationX, translationY表示view相對于父容器的偏移量至朗,默認值為0

topleft表示原始的位置信息屉符,在平移過程中,translationX爽丹、translationY發(fā)生改變筑煮,進而x, y改變辛蚊。

2.MotionEvent(位移事件)

ACTION_DOWN:手指剛接觸屏幕

ACTION_MOVE:手指在屏幕上移動

ACTION_UP:手指離開屏幕

getX(),?getY()點擊事件相對于當前View的左上角的xy坐標getRawX(), getRawY()點擊事件相對于屏幕左上角的xy坐標

3.TouchSlop系統(tǒng)能識別的最小滑動距離

通過ViewConfiguration.get(getContext()).getScaledTouchSlop獲取

4.VelocityTracker追蹤手指滑動過程中的速度

onTouchEvent中追蹤點擊事件的速度

VelocityTrackervelocityTracker = VelocityTracker.obtain();

velocityTracker.addMovement(event);

velocityTracker.computeCurrentVelocity(1000),?1000ms內劃過的像素

velocityTracker.getXVelocity();水平速度

velocityTracker.getXVelocity();豎直速度

velocityTracker.clear();

velocityTracker.recycle();

5.GestureDetector手勢檢測檢測單擊粤蝎、雙擊、長按袋马、滑動

onSingleTapUp(單擊)初澎、onFailing(快速滑動)onScroll(拖動)onLongPress(長按)碑宴、onDoubleTap(雙擊)

6.Scroller彈性滑動對象有過渡效果的滑動

View的滑動

1.scrollTo& scrollBy

scrollTo()方法
scrollBy()方法

關鍵屬性mScrollX,mScrollY分別通過getScrollX()getScrollY()得到

mScrollXView左邊緣與View內容左邊緣在水平方向的距離

mScrollYView上邊緣與View內容上邊緣在豎直方向的距離

View左移時软啼,mScrollX為正,反之為負

View上移時延柠,mScrollY為正祸挪,反之為負

scrollToscrollBy只能改變View內容的位置,不能改變View在布局中的位置贞间。

2.使用動畫View動畫和屬性動畫

View動畫向右下角平移100

View動畫

View動畫是對View的影像做操作贿条,并沒有真正改變View的位置參數,動畫完成后結果會消失增热,設置fillAfter屬性為true會保留動畫后的狀態(tài)整以,移動后的View無法觸發(fā)onClick事件

屬性動畫View從原位置向右平移100像素

屬性動畫

屬性動畫后動畫結果不會消失,并且在平移動畫之后峻仇,能夠觸發(fā)onClick事件

3.改變布局參數

改變LayoutParams公黑,通過設置margin參數即可達到移動的效果。

改變LayoutParams

scrollTo/scrollBy:適合對View內容的滑動

動畫:適合沒有交互的View和復雜的動畫效果

改變布局參數:適合有交互的View

彈性滑動

1.探究Scoller

當構造一個Scroller對象并調用它的startScroll方法時摄咆,Scroller內部其實什么也沒做凡蚜,只是傳遞了幾個參數并保存了,而invalidate方法會導致View重繪豆同,draw方法又會調用computeScroll方法番刊,而在computeScroll中通過scrollTo方法讓View滑動,接著又調用postInvalidate方法再次重繪影锈,直到滑動過程結束芹务。

而在computeScroll方法中,通過computeScrollOffset判斷當前是否重繪結束鸭廷,通過時間的流逝來計算當前View的位置坐標枣抱。如果經過的時間小于總時間,繼續(xù)重繪辆床,直到時間達到動畫的總時間佳晶,重繪結束,同時滑動完成讼载。

View的重繪距滑動起始會有一個時間間隔轿秧,通過這個時間間隔就能得出當前View的滑動位置,然后通過scrollTo方法完成View的滑動咨堤,這樣每次重繪都會讓View小幅度滑動菇篡,多次就成了彈性滑動。

2.使用動畫

通過動畫的每一幀的到來獲取動畫完成的比例一喘,然后根據這個比例計算View應該滑動的距離驱还。

動畫滑動

3.延時策略

通過HandlerpostDelayed,或者在線程中通過whilesleep來不斷的發(fā)送消息,在消息中進行View的滑動议蟆。

View的事件分發(fā)機制

1.傳遞規(guī)則

分發(fā)過程即將MotionEvent傳遞到一個具體的View闷沥,由三個重要的

方法共同完成,dispatchTouchEvent咐容,onInterceptTouchEvent舆逃,onTouchEvent

public booleandispatchTouchEvent(MotionEvent ev)

事件傳遞給當前View一定會執(zhí)行這個方法戳粒,返回結果與ViewonTouchEvent和下級ViewdispatchTouchEvent有關颖侄,表示是否消耗當前的事件。

public booleanonInterceptTouchEvent(MotionEvent ev)

判斷是否攔截某個事件享郊,在dispatchTouchEvent方法中調用览祖。如果View攔截了某個事件,那么在同一事件序列當中炊琉,該方法不會再被調用

public boolean onTouchEvent(MotionEventev)

處理點擊事件展蒂,判斷是否消耗了事件,如果不消耗苔咪,那在同一事件序列中锰悼,當前View不會再接收到事件。

這三個方法的關系可以用以下偽代碼表示

事件分發(fā)的關系

事件的傳遞規(guī)則:對于根ViewGroup來說团赏,點擊事件首先會傳給它箕般,此時調用它的dispatchTouchEvent方法,然后執(zhí)行onInterceptTouchEvent方法舔清,如果該方法返回true丝里,表示它要攔截該事件,然后事件就會交給ViewGroup處理体谒,然后它的onTouchEvent方法就會被調用杯聚,如果返回false表示它不攔截當前事件,這時事件就會傳遞給它的子元素抒痒,接著調用子元素的dispatchTouchEvent方法幌绍,如此反復直到事件被處理。

監(jiān)聽事件優(yōu)先級OnTouchListener > OnTouchEvent > OnclickListener

當產生點擊事件后故响,總是先傳遞給Activity傀广,再傳遞給Window,最后傳遞給頂級View彩届,然后按照事件的傳遞規(guī)則去分發(fā)事件伪冰。如果所有View都不處理這個事件,那么這個事件最終會傳遞給Activity處理惨缆,ActivityonTouchEvent方法會被調用糜值。

總結

a.同一個時間序列是指從手指接觸屏幕開始,到手指離開屏幕坯墨,中間有一系列的move事件

b.一般一個事件序列只能被一個View攔截消耗寂汇,因為一旦View截了某事件,那么同一事件序列內的所有事件都會直接交給它處理捣染,但是View可以將本應自己處理的事件通過onTouchEvent強行傳遞給其它View處理骄瓣。

c.某個View一旦攔截那么該事件序列都只能由它來處理,并且它onInterceptTouchEvent不會被調用耍攘。即不會再被攔截榕栏。

d.某個View一旦開始處理事件,如果不消耗ACTION_DOWN件蕾各,即onTouchEvent返回了false扒磁,那么同一事件序列中的其它事件不會再交給它處理,而是由它的父元素處理式曲,即父元素的onTouchEvent會被調用妨托。

e.某個View不消耗除ACTION_DOWN以外的其它事件,那么這個點擊事件會消失吝羞,但父元素的onTouchEvent不會被調用兰伤,并且當前View會持續(xù)收到后續(xù)事件,最終傳遞給Activity處理钧排。

f.ViewGroup默認不攔截任何事件敦腔,即onInterceptTouchEvent默認返回false

g.View沒有onInterceptTouchEvent方法恨溜,一旦事件傳遞給它符衔,那么它的onTouchEvent方法就會被調用。

h.ViewonTouchEvent方法默認都是消耗事件糟袁,除非它是不可點擊的(clickablelongClickable都為false柏腻,ViewlongClickable默認為false)。

i.事件的傳遞過程是由外向內的系吭,總是由父元素開始五嫂,再傳遞到子View,子View可以通過requestDisallowInterceptTouchEvent干預事件的分發(fā)肯尺,但是ACTION_DOWN除外沃缘。

2.源碼解析

1.Activity的分發(fā)過程

當點擊操作發(fā)生時,事件最先傳遞給當前的Activity则吟,由ActivitydispatchTouchEvent進行事件派發(fā)槐臀,具體是由Activity內的Window來完成的,Window會將事件傳遞給decor view(setContentView設置的View的父容器)氓仲。


Activity#dispatchTouchEvent

2.Window是如何將事件傳遞給ViewGroup

Window是個抽象類水慨,WindowsuperDispatchTouchEvent方法也是個抽象方法得糜,PhoneWindowWindow的唯一實現(xiàn)類。

PhoneWindow#dispatchTouchEvent

PhoneWindow直接將事件傳給了DecorView晰洒,DecorViewsetContentView所設置View的父容器朝抖,所以事件肯定會傳遞給setContentViewView,也叫頂級View谍珊。

3.頂級View對事件的分發(fā)過程

對于根ViewGroup來說治宣,點擊事件首先會傳給它,此時調用它的dispatchTouchEvent方法砌滞,然后執(zhí)行onInterceptTouchEvent方法侮邀,如果該方法返回true,表示它要攔截該事件贝润,然后事件就會交給ViewGroup處理绊茧,然后它的onTouchEvent方法就會被調用,如果返回false表示它不攔截當前事件打掘,這時事件就會傳遞給它的子元素按傅,接著調用子元素的dispatchTouchEvent方法,如此反復直到事件被處理胧卤。

這個方法較長唯绍,我們分段來看

ViewGroup#dispatchTouchEvent

ViewGroup的子元素成功處理時,mFirstTouchTarget會被賦值枝誊,并指向子元素况芒,即ViewGroup不攔截事件并交給子元素處理時mFirstTouchTarget != null

ViewGroup攔截時叶撒,mFirstTouchTarget就為空绝骚,當后續(xù)事件到來時actionMasked != MotionEvent.ACTION_DOWN并且mFirstTouchTarget== null,所以該條件為false祠够,所以ViewGrouponInterceptTouchEvent不會再被調用压汪,并且同一事件序列中的其它事件都將交給它來處理。

特殊情況:標記位FLAG_DISALLOW_INTERCEPT古瓤,子View可以通過設置requestDisallowInterceptTouchEvent方法止剖,使得ViewGroup無法攔截除了ACTION_DOWN以外的其它事件。如果是ACTION_DOWN事件落君,這個標記位就會被重置穿香。

重置標記位

上面代碼說明了ViewGroup會在ACTION_DOWN事件到來之前重置標記位的狀態(tài)。當ViewGroup不再攔截事件的時候绎速,事件就會下發(fā)至它的子View處理皮获。源碼如下所示

遍歷ViewGroup所有子元素,判斷子元素是否能夠接收點擊事件并且點擊事件是否落在子元素的區(qū)域內纹冤。如果都滿足洒宝,那么事件就交給它來處理购公,在dispatchTransformedTouchEvent方法中有這樣一段代碼。

dispatchTransformedTouchEvent

如果子元素不為空雁歌,直接調用它的dispatchTouchEvent方法宏浩,如果返回true,那么mFirstTouchEvent會被賦值并且跳出循環(huán)将宪。如果返回falseViewGroup會把事件分發(fā)給下一個元素橡庞。

如果遍歷完所有的元素后時間都沒有被處理较坛,那么有兩種情況,一是ViewGroup沒有子元素扒最,二是子元素處理了點擊事件丑勤,但是dispatchTouchEvent返回了false,一般是因為在TouchEvent中返回了false吧趣,這兩種情況下ViewGroup會自己處理點擊事件法竞。

這里dispatchTransformedTouchEvent方法中第三個參數為null,由之前的代碼分析强挫,它會調用super.dispatchTouchEvent方法岔霸,即交給View處理,接著看View的處理俯渤。

4.View對點擊事件的處理過程呆细。

View#dispatchTouchEvent
View#dispatchTouchEvent

首先判斷有沒有OnTouchListener,如果OnTouchListener中的onTouch方法返回true八匠,那么onTouchEvent就不會被調用絮爷,所以OnTouchListener優(yōu)先級高于onTouchEvent

接下來分析onTouchEvent梨树,這里面主要分三個過程: View處于不可用狀態(tài)坑夯,View設置有TouchDelegateView處于可用狀態(tài)抡四。

a.View不可用

View在不可用狀態(tài)下依然會消耗點擊事件

b.View設置了TouchDelegate

TouchDelegate可以讓某個控件處理比它實際占用空間更大的觸

摸消息柜蜈。具體可以參考官方文檔:TouchDelegate,具體使用:TouchDelegate的使用指巡。

c.View可用

整個過程分為ACTION_UP跨释,ACTION_DOWNACTION_CANCEL厌处,ACTION_MOVE四個狀態(tài)鳖谈,我們就最有代表性的ACTION_UP來分析

ACTION_UP

只要ViewCLICKABLELONG_CLICKABLEtrue,就會消耗這個事件阔涉,并返回true缆娃,然后觸發(fā)performClick方法捷绒,如果View設置了onClick事件,那么onClick就會被調用贯要。

performClick

可點擊的ViewCLICKABLEtrue暖侨,不可點擊的為false

ViewLONG_CLICKABLE默認為false

setClickablesetLongClickable可以分別改變其屬性。

setOnClickListenersetOnLongClickListener可以分別將CLICKABLELONG_CLICKABLE改為true崇渗。

到這里View的事件體系基本就分析完了字逗,以上所有內容參考自Android開發(fā)藝術探索。

2017.2.16 ?21:39

------END

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末宅广,一起剝皮案震驚了整個濱河市葫掉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跟狱,老刑警劉巖俭厚,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驶臊,居然都是意外死亡挪挤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門关翎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扛门,“玉大人,你說我怎么就攤上這事纵寝〖夥桑” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵店雅,是天一觀的道長政基。 經常有香客問我,道長闹啦,這世上最難降的妖魔是什么沮明? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮窍奋,結果婚禮上荐健,老公的妹妹穿的比我還像新娘。我一直安慰自己琳袄,他們只是感情好江场,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窖逗,像睡著了一般址否。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碎紊,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天佑附,我揣著相機與錄音樊诺,去河邊找鬼。 笑死音同,一個胖子當著我的面吹牛词爬,可吹牛的內容都是我干的。 我是一名探鬼主播权均,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼顿膨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叽赊?” 一聲冷哼從身側響起恋沃,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛇尚,沒想到半個月后芽唇,有當地人在樹林里發(fā)現(xiàn)了一具尸體顾画,經...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡取劫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了研侣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谱邪。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖庶诡,靈堂內的尸體忽然破棺而出惦银,到底是詐尸還是另有隱情,我是刑警寧澤末誓,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布扯俱,位于F島的核電站,受9級特大地震影響喇澡,放射性物質發(fā)生泄漏迅栅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一晴玖、第九天 我趴在偏房一處隱蔽的房頂上張望读存。 院中可真熱鬧,春花似錦呕屎、人聲如沸让簿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尔当。三九已至,卻和暖如春蹂安,著一層夾襖步出監(jiān)牢的瞬間居凶,已是汗流浹背虫给。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侠碧,地道東北人抹估。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像弄兜,于是被迫代替她去往敵國和親药蜻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內容