自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android中的事件分發(fā)和處理

移動(dòng)開發(fā) Android
上次跟大家分享了一下自定義View的一下要點(diǎn),這次跟大家聊一下View的事件分發(fā)及處理,為什么主題都是View,因?yàn)樽鳛橐幻跫?jí)應(yīng)用層Android工程師,跟我打交道最多的莫過于各種各樣的View,只有詳細(xì)了解他們各自的習(xí)性,才能更好地跟他們溝通交流,做出自己想要的效果。

上次跟大家分享了一下自定義View的一下要點(diǎn),這次跟大家聊一下View的事件分發(fā)及處理,為什么主題都是View,因?yàn)樽鳛橐幻跫?jí)應(yīng)用層Android工程師,跟我打交道最多的莫過于各種各樣的View,只有詳細(xì)了解他們各自的習(xí)性,才能更好地跟他們溝通交流,做出自己想要的效果。

基礎(chǔ)儲(chǔ)備 View、MotionEvent

我們都能詳細(xì)地說出Android的四大組件:Activity,Service,ContentProvider和BoardcastReceiver,但是四大組件之外,我們用到也很多的是什么,其中肯定包括View,View是用戶跟程序溝通的入口,也是程序展現(xiàn)給用戶信息的窗口。關(guān)于View,一些基礎(chǔ)屬性還是要了解的,left,top,right,bottom,分別代表了view的左上角和右下角分別相對(duì)x軸,y軸的坐標(biāo),而且view的getWidth和getHeight的值都是通過這四個(gè)值算得,而且在Android3.0中還增加了x,y,translationX和translationY這幾個(gè)屬性,便于我們對(duì)view的平移操作,x、y代表了當(dāng)前view左上角的xy坐標(biāo),而translationX和translationY代表了view相對(duì)它的父容器的偏移量,默認(rèn)值是0。

MotionEvent表示用戶的觸摸事件,用戶的一次點(diǎn)擊、觸摸或者滑動(dòng)都會(huì)產(chǎn)生一系列的MotionEvent:

  • MotionEvent.ACTION_DOWN 表示用戶的手指剛接觸到屏幕
  • MotionEvent.ACTION_MOVE 表示用戶的手指正在移動(dòng)
  • MotionEvent.ACTION_UP 表示用戶的手指從屏幕上抬起

所以一次用戶觸摸屏幕可能會(huì)產(chǎn)生這些事件:

  • 點(diǎn)擊屏幕然后松開,Down->Up
  • 點(diǎn)擊屏幕,然后滑動(dòng)一段距離,松開屏幕 ,Down->Move->…->Move->Up

了解了這些基本知識(shí)以后,我們就來學(xué)習(xí)一下具體怎么分發(fā)這些事件

ViewGroup 分發(fā)-> 攔截 -> 處理

首先說一點(diǎn),雖然ViewGroup也是繼承View而來,但是因?yàn)樵谑录r截上,ViewGroup分析起來更加方便理解,所以先說ViewGroup,下面也會(huì)簡單介紹一下View的事件處理。

在事件分發(fā)的過程中,主要涉及到三個(gè)方法:

  • dispatchTouchEvent(MotionEvent event);
  • onInterceptTouchEvent(MotionEvent event);
  • onTouchEvent();

初看這三個(gè)方法就有蒙圈,如果這時(shí)候在蒙頭鉆進(jìn)源碼里,就更是糊涂,我在這里借用任玉剛大大的一段偽代碼解釋一下這三者之間的關(guān)系:

  1. public boolean dispatchTouchEvent(MotionEvent event) { 
  2.  
  3.         boolean consume = false
  4.  
  5.         if (onInterceptTouchEvent(event)) { 
  6.  
  7.             consume = onTouchEvent(event); 
  8.  
  9.         } else { 
  10.  
  11.             consume = child.dispatchTouchEvent(event); 
  12.  
  13.         } 
  14.  
  15.   
  16.  
  17.         return consume; 
  18.  
  19.     }  

 

 

 

 

  • 從這段偽代碼中,我們可以看出來,在dispatchTouchEvent中,先調(diào)用ViewGroup自身的onInterceptTouchEvent方法,判斷自己是否要攔截,如果這時(shí)候自己攔截,那就調(diào)用自己的onTouchEvent方法,如果onTouchEvent方法返回了True,那么這次的事件就算消耗了,事件傳遞到此為止,如果返回了False,證明這次沒有消耗這次MotionEvent,那么這次的事件就會(huì)往上返回,由上一級(jí)繼續(xù)處理;如果當(dāng)前ViewGroup的onInterceptTouchEvent返回了False,那就會(huì)調(diào)用它的子view的dispatchTouchEvent方法,這樣這個(gè)事件就傳遞下去了,如果它的子View處理不了,那么還會(huì)回來調(diào)用ViewGroup的onTouchEvent方法,當(dāng)然這一點(diǎn)是沒有在這一段偽代碼里體現(xiàn)的,用一段通俗的例子解釋:

領(lǐng)導(dǎo)收到一份任務(wù)(有可能是上級(jí)給的),自己看了一眼,然后決定好好休息,今天不工作,就把這個(gè)任務(wù)交給了手下的小王,小王的默認(rèn)屬性是只要來任務(wù)就接,而且就干,能干不能干一樣接,如果這是一個(gè)簡單的任務(wù),那么小王就解決了,這個(gè)任務(wù)也就完成了,不幸的是,這次任務(wù)小王沒有解決掉,然后向領(lǐng)導(dǎo)反饋,領(lǐng)導(dǎo)沒辦法,手下沒人能解決,只好自己干了,就開始解決問題,然后解決掉,任務(wù)也完成了。

這就是ViewGroup層的事件分發(fā),當(dāng)然不是這么簡單,這只不過是通過簡單的方式去理解,其實(shí)在真實(shí)的事件分發(fā)中,有很多問題需要注意:

  • 一個(gè)完成的事件序列以Down開始,中間可能包含若干個(gè)Move,然后以Up結(jié)束
  • 一個(gè)view一旦攔截一個(gè)某個(gè)事件,當(dāng)前事件所在的完整事件序列將都會(huì)由這個(gè)view去處理,反應(yīng)在真實(shí)的代碼中,就是一旦view攔截了down事件,那么此后的move和up事件都將不調(diào)用onInterceptTouchEvent,而直接由它處理,這就也意味著在onInterceptTouchEvent處理事件是不合適的,因?yàn)橛锌赡軄砹耸录?,卻直接跳過onInterceptTouchEvent方法。這個(gè)也意味著,一旦一個(gè)ViewGroup沒有攔截ACTION_DOWN,那么這個(gè)事件序列的其他Action,它都將收不到,所以在處理ACTION_DOWN的時(shí)候,尤其需要謹(jǐn)慎。
  • onTouchEvent中是要判斷MotionEvent的Action,因?yàn)橐淮吸c(diǎn)擊操作就會(huì)調(diào)用兩次onTouchEvent方法,一次是ACTION_DOWN,一次是ACTION_UP,如果手滑一下,還會(huì)有若干個(gè)ACTION_MOVE
  • ViewGroup默認(rèn)不攔截任何事件,源碼中ViewGroup的onInterceptTouchEvent方法默認(rèn)返回的是false

整個(gè)事件分發(fā),看起來都是由外向內(nèi)傳遞的,父View將事件傳遞給子View,理論上來看,子View是沒有辦法影響到父View的事件處理的,但是有一個(gè)標(biāo)示位,requestDisallowInterceptTouchEvent方法,通過這個(gè)方法 ,子View能夠影響父view的事件處理,這個(gè)可以用于解決父view和子view的滑動(dòng)沖突,具體想了解的可以搜索它的相關(guān)用法,這里將不進(jìn)行展開。

View 只有默默的承受

View不同于ViewGroup的是,View中沒有onInterceptTouchEvent方法,因?yàn)閂iew作為事件處理的***一級(jí),不需要判斷是否要攔截,是一定要攔截,不管能不能處理,都要試一下,所以在View中調(diào)用流程是:

dispatchTouchEvent -> onTouchEvent

而且,***onTouchEvent的返回值默認(rèn)都是True,也就是說事件傳遞下去一般都會(huì)被消耗掉的,只是看中途是否有人攔截,這個(gè)時(shí)候讀者可能會(huì)有疑問:TextView的onTouchEvent的返回值也是True嗎?答案就是:是的,那為什么點(diǎn)在TextView上面還是能觸發(fā)它的父視圖的onTouchEvent,理論上不應(yīng)該是,TextView消耗掉這次的事件,不回傳。理論上確實(shí)是這樣,但是因?yàn)門extView的clickable和longClickable屬性都是false,當(dāng)這兩個(gè)屬性都為false的時(shí)候,是不會(huì)消耗事件的,所以TextView不會(huì)消耗事件,這也就可以解釋為什么把一個(gè)TextView放在一個(gè)Button上面,然后點(diǎn)擊TextView還是能觸發(fā)Button的點(diǎn)擊事件

在這里可能需要提醒一下大家,算是一個(gè)我之前踩到的一個(gè)坑,我把一個(gè)view的enable狀態(tài)設(shè)成了false,然后又給它增加了onClickListener,這時(shí)候我本以為,它的點(diǎn)擊事件不會(huì)被觸發(fā),結(jié)果它還是可以被點(diǎn)擊,后來才了解到,view的enable狀態(tài)和onTouchEvent是沒有關(guān)系的,只有clickable狀態(tài)是對(duì)onTouchEvent有影響的,還有一點(diǎn) ,設(shè)置 view的enable為false確實(shí)也會(huì)把view的clickable設(shè)成false,但是設(shè)置view的onclickListener就又把view的clickable變成了true,所以***的解決方案就是把那兩行代碼換下先后順序,問題就迎刃而解了。

詳解處理GesutureDetector

費(fèi)勁千辛萬苦,終于把事件攔截下來了,然后我們需要總得做點(diǎn)什么吧,不然都對(duì)不起自己浪費(fèi)這么多口舌,說到對(duì)事件的處理,我們首先想到的就是setOnClickListener,殊不知onClickListener的優(yōu)先級(jí)是***的,下一節(jié)里面會(huì)對(duì)優(yōu)先級(jí)進(jìn)行說明,而這里,我們將主要想著如果處理事件,當(dāng)我們興奮地拿到一連串的事件,但又不知如何下手,甚至于連最簡單的點(diǎn)擊事件都要自己進(jìn)行一番處理,更別提做成平移、旋轉(zhuǎn)、縮放這樣的操作,但是官方提供的GestureDetector給我們提供了可能。

官方提供的GestureDetector是一個(gè)手勢(shì)輔助檢測(cè)類,默認(rèn)能夠檢測(cè)多種手勢(shì):

  1. class SimpleGestureListener implements GestureDetector.OnGestureListener { 
  2.  
  3.     @Override 
  4.  
  5.     public boolean onDown(MotionEvent e) { 
  6.  
  7.         return false
  8.  
  9.     } 
  10.  
  11.   
  12.  
  13.     @Override 
  14.  
  15.     public void onShowPress(MotionEvent e) { 
  16.  
  17.   
  18.  
  19.     } 
  20.  
  21.   
  22.  
  23.     @Override 
  24.  
  25.     public boolean onSingleTapUp(MotionEvent e) { 
  26.  
  27.         return false
  28.  
  29.     } 
  30.  
  31.   
  32.  
  33.     @Override 
  34.  
  35.     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 
  36.  
  37.         return false
  38.  
  39.     } 
  40.  
  41.   
  42.  
  43.     @Override 
  44.  
  45.     public void onLongPress(MotionEvent e) { 
  46.  
  47.   
  48.  
  49.     } 
  50.  
  51.   
  52.  
  53.     @Override 
  54.  
  55.     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 
  56.  
  57.         return false
  58.  
  59.     } 
  60.  
  61.  

通過這個(gè)類,我們可以很方便的處理除了單擊和長按之外,還有滑動(dòng),雙擊等各種手勢(shì),并對(duì)其分別進(jìn)行處理,如果這些還是不能滿足你的好奇心,那還有一個(gè)官方提供的ScaleGestureDetector,從名字就可以判斷出來這是一個(gè)檢測(cè)縮放手勢(shì)的輔助類,而且還有大牛仿照ScaleGestureDetector思路做出了平移以及旋轉(zhuǎn)的輔助類,然后我們就可以根據(jù)這些輔助類,幾乎為所欲為了,下面我寫了一個(gè)支持平移,縮放,旋轉(zhuǎn)的小Demo。

  1. private void init() { 
  2.  
  3.   
  4.  
  5.         scaleGesture = new ScaleGestureDetector(getContext(), new ScaleListener()); 
  6.  
  7.         moveGesture = new MoveGestureDetector(getContext(), new MovingListener()); 
  8.  
  9.         rotateGesture = new RotateGestureDetector(getContext(), new RotateListener()); 
  10.  
  11.   
  12.  
  13.     } 
  14.  
  15.   
  16.  
  17.     @Override 
  18.  
  19.     public boolean onTouchEvent(MotionEvent event) { 
  20.  
  21.   
  22.  
  23.         scaleGesture.onTouchEvent(event); 
  24.  
  25.         moveGesture.onTouchEvent(event); 
  26.  
  27.         rotateGesture.onTouchEvent(event); 
  28.  
  29.   
  30.  
  31.         return true
  32.  
  33.     } 
  34.  
  35.   
  36.  
  37.   
  38.  
  39.     private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener { 
  40.  
  41.   
  42.  
  43.         @Override 
  44.  
  45.         public boolean onScale(ScaleGestureDetector detector) { 
  46.  
  47.   
  48.  
  49.             setScaleX(detector.getScaleFactor() * getScaleX()); 
  50.  
  51.             setScaleY(detector.getScaleFactor() * getScaleY()); 
  52.  
  53.   
  54.  
  55.             return true
  56.  
  57.         } 
  58.  
  59.   
  60.  
  61.         @Override 
  62.  
  63.         public boolean onScaleBegin(ScaleGestureDetector detector) { 
  64.  
  65.             return true
  66.  
  67.         } 
  68.  
  69.   
  70.  
  71.         @Override 
  72.  
  73.         public void onScaleEnd(ScaleGestureDetector detector) { 
  74.  
  75.   
  76.  
  77.         } 
  78.  
  79.     }  

 

 

 

 

只貼了部分的代碼,而且貌似旋轉(zhuǎn)好像還有點(diǎn)問題,以后時(shí)間再修正,有用到的讀者可以在詳細(xì)了解下,完整代碼,我會(huì)在文章的***給出鏈接,同時(shí)感謝Android multitouch gesture detectors的作者,提供了這么方便的手勢(shì)操作類

onTouchListener OnTouchEvent OnClickListener

我們?cè)谥纎nTouchEvent之前肯定都知道onClickListener和onTouchListener,而他們都是事件的消費(fèi)者,onTouchListener是在onTouch方法中生效,而且onTouch要先于onTouchEvent,就是說一旦設(shè)置了onTouchListener并且***onTouch方法返回了True,那onTouchEvent將不會(huì)再被執(zhí)行,而onClickListener和onTouchEvent有些關(guān)系,onTouchEvent的默認(rèn)實(shí)現(xiàn)里會(huì)調(diào)用onClickListener的onClick方法,如果重寫了onTouchEvent,因?yàn)閛nClickListener接受不到ACTION_DOWN和ACTION_UP,那么再設(shè)置onClickListener也就不會(huì)再生效了,這個(gè)時(shí)候的單擊或者長按處理只能在onTouchEvent中自己處理。 

責(zé)任編輯:龐桂玉 來源: 安卓開發(fā)精選
相關(guān)推薦

2023-10-08 08:23:44

Android事件邏輯

2024-07-01 08:27:05

KeyAndroid按鍵事件

2017-02-21 12:20:20

Android事件分發(fā)機(jī)制實(shí)例解析

2010-08-06 10:03:42

Flex事件

2016-12-08 10:19:18

Android事件分發(fā)機(jī)制

2021-08-17 13:41:11

AndroidView事件

2010-08-06 10:24:56

Flex事件分發(fā)

2012-12-28 14:53:34

Android開發(fā)初始化窗體事件

2010-08-09 09:47:34

Flex事件機(jī)制

2023-04-03 08:39:33

中間件go語言

2009-08-04 09:56:46

C#事件處理自定義事件

2023-03-14 07:31:17

EoscGo語言

2017-07-20 16:55:56

Android事件響應(yīng)View源碼分析

2021-08-20 09:48:07

鴻蒙HarmonyOS應(yīng)用

2011-06-16 14:23:43

JavaScript空事件處理程序

2021-02-06 10:27:45

C#函數(shù)參數(shù)

2021-08-11 14:29:20

鴻蒙HarmonyOS應(yīng)用

2020-09-25 11:10:51

運(yùn)維故障排查監(jiān)控

2024-06-28 10:19:02

委托事件C#

2021-10-28 17:05:11

IT運(yùn)維故障
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)