view的視圖架
每一個Activity都包含的一個window非剃,這個window的實(shí)現(xiàn)類是Phone Window置逻。后Phone Window是頂層的view,叫docor view备绽。docor view中有一個叫content的FrameLayout,我們經(jīng)常在Activity的onCreate中使用setContentView(R.layout.id)設(shè)置我們自定義的視圖券坞,就是添加到這個叫content的framelayout中。然后上面還有一個TitleActionBar肺素,這個就是TitleBar恨锚。
在content這個view中,是我們在xml中自定義的view倍靡。從圖中可以看出猴伶,我們自定義的view形成了一個樹形結(jié)構(gòu)。viewGroup中可以放置若干個view塌西,而由于viewGroup本身也繼承了view他挎,所以ViewGroup的子view也可以是另一個Viewgroup,這樣形成一個樹形結(jié)構(gòu)捡需。
MeasureSpec
這是一個很重要的概念办桨,一個view的MeasureSpec可以看作這個view暫定的大小。
MeasureSpec是一個32位的int值栖忠。前兩位表示測量模式崔挖,后兩位表示暫定的測量大小贸街。
測量模式有三種庵寞,分別是:UNSPECIFIED,即父容器對子容器的大小不作任何要求薛匪;EXACTLY捐川,父容器已經(jīng)確定了自容器的精確大小逸尖;AT_MOST古沥,父容器指定了一個最大的大小瘸右,view不能超過這個大小。
MeasureSpec由當(dāng)前view的parentView調(diào)用getChildMeasureSpec生成岩齿,傳入的三個參數(shù)分別是太颤,parentView的MeasureSpec(父容器的暫定大小)盹沈,parentView的左右上下內(nèi)邊距和當(dāng)前view的LayoutParams(自容器的期望大辛湔隆)。
/**
* Does the hard part of measureChildren: figuring out the MeasureSpec to
* pass to a particular child. This method figures out the right MeasureSpec
* for one dimension (height or width) of one child view.
*
* 這個方法將根據(jù)父容器的MeasureSpec和子View LayoutParams中的寬/高
* 為子View生成最合適的MeasureSpec
*
* @param spec 父容器的MeasureSpec
* @param padding 父容器的內(nèi)間距(padding)加上子View的外間距(margin)
* @param childDimension 子View的LayoutParams中封裝的width/height
* @return 子View的MeasureSpec
*/?
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
? ? // ① 對父容器的MeasureSpec進(jìn)行解包
? ? int specMode = MeasureSpec.getMode(spec);
? ? int specSize = MeasureSpec.getSize(spec);
? ? // ② 減去間距乞封,得到最大可用空間
? ? int size = Math.max(0, specSize - padding);
? ? // 記錄子View最終的大小和測量模式
? ? int resultSize = 0;
? ? int resultMode = 0;
? ? switch (specMode) {
? ? // ③ 父容器是精準(zhǔn)測量模式
? ? case MeasureSpec.EXACTLY:
? ? ? ? if (childDimension >= 0) {? ? ? ? ? ? ? //如果子view在LayoutParam中指定了大小做裙,那么子view的resultSize 就是該大小,模式是EXACTLY
? ? ? ? ? ? resultSize = childDimension;
? ? ? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? ? ? } else if (childDimension == LayoutParams.MATCH_PARENT) {? ? ? //如果子view中LayoutParams是MATCH_PARENT肃晚,則view的大小等于最大可用大小锚贱,測量模式是EXACTLY
? ? ? ? ? ? resultSize = size;
? ? ? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? ? ? } else if (childDimension == LayoutParams.WRAP_CONTENT) {? //如果子view中LayoutParams是WRAP_CONTENT,則view的大小等于最大可用大小关串,測量模式是AT_MOST
? ? ? ? ? ? resultSize = size;
? ? ? ? ? ? resultMode = MeasureSpec.AT_MOST;
? ? ? ? }
? ? ? ? break;
? ? // ④ 父容器指定了一個最大可用的空間
? ? case MeasureSpec.AT_MOST:
? ? ? ? if (childDimension >= 0) {
? ? ? ? ? ? resultSize = childDimension;
? ? ? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? ? ? } else if (childDimension == LayoutParams.MATCH_PARENT) {
? ? ? ? ? ? resultSize = size;
? ? ? ? ? ? resultMode = MeasureSpec.AT_MOST;
? ? ? ? } else if (childDimension == LayoutParams.WRAP_CONTENT) {
? ? ? ? ? ? resultSize = size;
? ? ? ? ? ? resultMode = MeasureSpec.AT_MOST;
? ? ? ? }
? ? ? ? break;
? ? // ⑤ 父容器不對子View的大小作出限制
? ? case MeasureSpec.UNSPECIFIED:
? ? ? ? if (childDimension >= 0) {
? ? ? ? ? ? resultSize = childDimension;
? ? ? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? ? ? } else if (childDimension == LayoutParams.MATCH_PARENT) {
? ? ? ? ? ? resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
? ? ? ? ? ? resultMode = MeasureSpec.UNSPECIFIED;
? ? ? ? } else if (childDimension == LayoutParams.WRAP_CONTENT) {
? ? ? ? ? ? resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
? ? ? ? ? ? resultMode = MeasureSpec.UNSPECIFIED;
? ? ? ? }
? ? ? ? break;
? ? }
? ? // ⑥ 將最終的size和mode打包為子View需要的MeasureSpec
? ? return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
在view中每個子view的MeasureSpec都是由父容器的MeasureSpec拧廊、間距和自容器的LayoutParam來生成,逐級往上晋修,最頂層的view卦绣,也就是docor view的MeasureSpec是怎么生成的呢?docor view的MeasureSpec由其上層的window的大小和docor view的自身的LayoutParam來生成飞蚓。
自定義View 6步
1.繼承View
2.實(shí)現(xiàn)畫筆paint類
3.覆寫onMeasure(…)方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
? ? int w = MeasureSpec.getSize(widthMeasureSpec);
? ? int h = MeasureSpec.getSize(heightMeasureSpec);
? ? setMeasuredDimension(w, h);
}
4.實(shí)現(xiàn)onSizeChanged(…)方法
這個方法是你獲取View現(xiàn)在的寬和高. 這里我們計算的是中心和半徑滤港。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
????mCenterX = w / 2f;
????mCenterY = h / 2f;
????mRadius = Math.min(w, h) / 2f;
}
5.實(shí)現(xiàn)onDraw(…)方法
這個方法提供了如何繪制view,它提供的Canvas類可以進(jìn)行繪制趴拧。
@Override
protected void onDraw(Canvas canvas) {
? ? // draw face
? ? canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint);
? ? // draw eyes
? ? float eyeRadius = mRadius / 5f;
? ? float eyeOffsetX = mRadius / 3f;
? ? float eyeOffsetY = mRadius / 3f; canvas.drawCircle(mCenterX - eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius, mEyeAndMouthPaint); canvas.drawCircle(mCenterX + eyeOffsetX, mCenterY - eyeOffsetY, eyeRadius, mEyeAndMouthPaint);
? ? // draw mouth
? ? float mouthInset = mRadius /3f;
? ? mArcBounds.set(mouthInset, mouthInset, mRadius * 2 - mouthInset, mRadius * 2 - mouthInset);
? ? canvas.drawArc(mArcBounds, 45f, 90f, false, mEyeAndMouthPaint);
}
6.添加你的View到xml使用
7.自定義View的觸摸反饋
重寫onTouchEvent()溅漾,在方法內(nèi)部定制觸摸反饋算法
是否取消事件取決于ACTION_DOWN事件或PONITER_DOWN事件是否返回true
MotionEvent
getActionMasked()和getAction()
?POINTER_DOWN / POINTER_UP 和 getActionIndex()
onTouchEvent()
當(dāng)用戶按下(ACTION_DOWN)
? 如果不在滑動控件中,切換至按下狀態(tài)著榴,并注冊長按計時器
? ?如果在滑動控件中添履,切換至按下狀態(tài),并注冊按下計時器
? ? 當(dāng)進(jìn)入按下狀態(tài)并移動(ACTION_MOVE)
? ? 重繪Ripple Effect
? ? 如果移動出自己的范圍脑又,自我標(biāo)記本次事件失效暮胧,忽略后續(xù)事件
? ? 當(dāng)用戶抬起(ACTION_UP)
? ? 如果是按下狀態(tài)并且未觸發(fā)長按,切換至抬起狀態(tài)并觸發(fā)點(diǎn)擊事件问麸,并清除一切狀態(tài)
? ? 如果已經(jīng)觸發(fā)長按往衷,切換至抬起狀態(tài)并清除一切狀態(tài)
? ? 當(dāng)事件意外結(jié)束(ACTION_CANCEL)
? ? ?切換至抬起狀態(tài),并清除一切狀態(tài)
自定義ViewGroup 的觸摸反饋
除了重寫onTouchEvent()严卖,還需要重寫onInterceptEvent()
?onInterceptEvent() 不用在第一時間返回true席舍,而是在任意事件,需要攔截的時候返回true就行
觸摸反饋的流程
Activity.dispatchEvent()
? ? ? ? 遞歸:ViewGroup(View).dispatchTouchEvent()
? ? ? ? ? ? ViewGroup.onInterceptTouchEvent()
? ? ? ? ? ? ViewGroup.onIntercepTouchEvent()
? ? ? ? ? ? Child.dispatchTouchEvent()
? ? ? ? ? ? Super.dispatchEvent()
? ? ? ? ? ? ? ? View.onTouchEvent()
? ? ? ? Activity.onTouchEvent()
View.dispatchTouchEvent()
如果設(shè)置了OnTouchListener,調(diào)用OnTouchListener.onTouch()
? ? ? ? ? ? 如果OnTouchListener消費(fèi)了事件哮笆,返回true
? ? ? ? ? ? 如果onTouchListener沒有了消費(fèi)事件来颤,繼續(xù)調(diào)用自己的onTouchEvent()汰扭,并返回和onTouchEvent()相同的結(jié)果
? ? ? ? 如果沒有設(shè)置onTouchEvent,同上
ViewGroup.dispatchEvent()
如果是用戶初次按下(ACTION_DOWN),清空TouchTargets和DISALLOW_INTERCEPT標(biāo)記
? ? ? ? 攔截處理
? ? ? ? 如果不攔截并且不是CANCEL事件,并且是DOWN或者POINTER_DOWN福铅,嘗試把pointer(手指)通過TouchTarget分配給子View萝毛;并且如果分配給了新的子View,調(diào)用child.dispathTouchEvent()把事件傳遞給子View
? ? ? ? 看有沒有TouchTarget
? ? ? ? ? ? 如果沒有TouchTarget,調(diào)用自己的super.dispatchTouchEvent()
? ? ? ? ? ? 如果有滑黔,調(diào)用child.dispatchEvent()把事件傳給對應(yīng)的子View(如果有的話)
? ? ? ? ? ? 如果是POINTER_UP,從TouchTargets中清除POINTER信息珊泳,如果是UP或CANCEL,重置狀態(tài)
TouchTarget
作用:記錄每個子View是被哪些pointer(手指)按下的
? ? ? ? 結(jié)構(gòu):單向鏈表
攔截處理
如果不是初次按下拷沸,并且沒有TouchTarget色查,直接攔截
? ? ? ? 如果不是初次按下,或者有TouchTarget
? ? ? ? ? ? 如果設(shè)置了disallow intercept撞芍,不攔截
否則秧了,調(diào)用onInterceptTouchEvent(),如果返回true則攔截序无,返回false則不攔截
事件分發(fā)機(jī)制涉及兩個知識點(diǎn):1验毡、事件分發(fā)傳遞流程;2帝嗡、滑動沖突問題
仔細(xì)看的話晶通,圖分為3層,從上往下依次是 Activity哟玷、ViewGroup狮辽、View
事件從左上角那個白色箭頭開始,由 Activity 的 dispatchTouchEvent 做分發(fā)
箭頭的上面字代表方法返回值巢寡,(return true喉脖、return false、return super.xxxxx(),super 的意思是調(diào)用父類實(shí)現(xiàn)抑月。
dispatchTouchEvent 和 onTouchEvent 的框里有個【true---->消費(fèi)】的字树叽,表示的意思是如果方法返回 true,那么代表事件就此消費(fèi)谦絮,不會繼續(xù)往別的地方傳了题诵,事件終止。
目前所有的圖的事件是針對 ACTION_DOWN 的层皱,對于 ACTION_MOVE 和 ACTION_UP我們最后做分析性锭。
只有 return super.dispatchTouchEvent(ev) 才是往下走,返回true 或者 false 事件就被消費(fèi)了(終止傳遞)奶甘。
1篷店、如果事件不被中斷祭椰,整個事件流向是一個類U型圖臭家,我們來看下這張圖疲陕,可能更能理解U型圖的意思。
所以如果我們沒有對控件里面的方法進(jìn)行重寫或更改返回值钉赁,而直接用super調(diào)用父類的默認(rèn)實(shí)現(xiàn)蹄殃,那么整個事件流向應(yīng)該是從 Activity---->ViewGroup--->View 從上往下調(diào)用 dispatchTouchEvent方法,一直到葉子節(jié)點(diǎn)(View)的時候你踩,再由 View--->ViewGroup--->Activity 從下往上調(diào)用 onTouchEvent 方法诅岩。
2、dispatchTouchEvent 和 onTouchEvent 一旦 return true, 事件就停止傳遞了(到達(dá)終點(diǎn))(沒有誰能再收到這個事件)带膜》郧看下圖中只要 return true 事件就沒再繼續(xù)傳下去了,對于 return true 我們經(jīng)常說事件被消費(fèi)了膝藕,消費(fèi)了的意思就是事件走到這里就是終點(diǎn)式廷,不會往下傳,沒有誰能再收到這個事件了芭挽。
3滑废、dispatchTouchEvent 和 onTouchEvent return false 的時候事件都回傳給父控件的onTouchEvent 處理。
看上圖深藍(lán)色的線袜爪,對于返回 false 的情況蠕趁,事件都是傳給父控件 onTouchEvent 處理。
對于 dispatchTouchEvent 返回 false 的含義應(yīng)該是:事件停止往子 View 傳遞和分發(fā)同時開始往父控件回溯(父控件的 onTouchEvent 開始從下往上回傳直到某個 onTouchEvent return true)辛馆,事件分發(fā)機(jī)制就像遞歸俺陋,return false 的意義就是遞歸停止然后開始回溯。
對于onTouchEvent return false 就比較簡單了昙篙,它就是不消費(fèi)事件倔韭,并讓事件繼續(xù)往父控件的方向從下往上流動。
4瓢对、dispatchTouchEvent寿酌、onTouchEvent、onInterceptTouchEvent ViewGroup 和 View 的這些方法的默認(rèn)實(shí)現(xiàn)就是會讓整個事件安裝 U 型完整走完硕蛹,所以 return super.xxxxxx() 就會讓事件依照U型的方向的完整走完整個事件流動路徑)醇疼,中間不做任何改動,不回溯法焰、不終止秧荆,每個環(huán)節(jié)都走到。
所以如果看到方法 return super.xxxxx() 那么事件的下一個流向就是走 U 型下一個目標(biāo)埃仪,稍微記住上面這張圖乙濒,你就能很快判斷出下一個走向是哪個控件的哪個函數(shù)。
5、onInterceptTouchEvent 的作用
Intercept 的意思就攔截颁股,每個 ViewGroup 每次在做分發(fā)的時候么库,問一問攔截器要不要攔截(也就是問問自己這個事件要不要自己來處理)如果要自己處理那就在 onInterceptTouchEvent 方法中 return true 就會交給自己的 onTouchEvent 的處理,如果不攔截就是繼續(xù)往子控件往下傳甘有。默認(rèn)是不會去攔截的诉儒,因?yàn)樽?View 也需要這個事件,所以 onInterceptTouchEvent 攔截器 return super.onInterceptTouchEvent() 和r eturn false 是一樣的亏掀,是不會攔截的忱反,事件會繼續(xù)往子 View 的 dispatchTouchEvent 傳遞。
6滤愕、ViewGroup 和View 的dispatchTouchEvent方法返回super.dispatchTouchEvent()的時候事件流走向温算。
首先看下 ViewGroup 的 dispatchTouchEvent,之前說的 return true 是終結(jié)傳遞间影。return false 是回溯到父 View 的 onTouchEvent米者,然后 ViewGroup 怎樣通過 dispatchTouchEvent 方法能把事件分發(fā)到自己的 onTouchEvent 處理呢,return true 和 false 都不行宇智,那么只能通過 Interceptor 把事件攔截下來給自己的 onTouchEvent蔓搞,所以 ViewGroup dispatchTouchEvent 方法的 super 默認(rèn)實(shí)現(xiàn)就是去調(diào)用 onInterceptTouchEvent艾帐,記住這一點(diǎn)蛉签。
那么對于 View 的 dispatchTouchEvent return super.dispatchTouchEvent() 的時候呢事件會傳到哪里呢沟涨,很遺憾 View 沒有攔截器瓷胧。但是同樣的道理 return true 是終結(jié)聂示。return false 是回溯會父類的 onTouchEvent俊马,怎樣把事件分發(fā)給自己的 onTouchEvent 處理呢呆瞻,那只能 return super.dispatchTouchEvent,View 類的 dispatchTouchEvent() 方法默認(rèn)實(shí)現(xiàn)就是能幫你調(diào)用 View 自己的 onTouchEvent 方法的峭范。
說了這么多萝嘁,不知道有說清楚沒有梆掸,我這邊最后總結(jié)一下:
對于 dispatchTouchEvent,onTouchEvent牙言,return true是終結(jié)事件傳遞酸钦。return false 是回溯到父 View 的 onTouchEvent 方法。
ViewGroup 想把自己分發(fā)給自己的 onTouchEvent咱枉,需要攔截器 onInterceptTouchEvent 方法 return true 把事件攔截下來卑硫。
ViewGroup 的攔截器 onInterceptTouchEvent 默認(rèn)是不攔截的,所以 return super.onInterceptTouchEvent()=return false蚕断;
View 沒有攔截器欢伏,為了讓View可以把事件分發(fā)給自己的 onTouchEvent,View的 dispatchTouchEvent 默認(rèn)實(shí)現(xiàn)(super)就是把事件分發(fā)給自己的 onTouchEvent亿乳。
ViewGroup 和 View 的 dispatchTouchEvent 是做事件分發(fā)硝拧,那么這個事件可能分發(fā)出去的四個目標(biāo)
注:------> 后面代表事件目標(biāo)需要怎么做。
1、 自己消費(fèi)障陶,終結(jié)傳遞滋恬。------->return true ;
2咸这、 給自己的 onTouchEvent 處理-------> 調(diào)用 super.dispatchTouchEvent() 系統(tǒng)默認(rèn)會去調(diào)用 onInterceptTouchEvent夷恍,在onInterceptTouchEvent return true就會去把事件分給自己的 onTouchEvent 處理魔眨。
3媳维、 傳給子 View ------>調(diào)用 super.dispatchTouchEvent() 默認(rèn)實(shí)現(xiàn)會去調(diào)用 onInterceptTouchEvent 在 onInterceptTouchEvent return false,就會把事件傳給子類遏暴。
4侄刽、 不傳給子 View,事件終止往下傳遞朋凉,事件開始回溯州丹,從父 View 的 onTouchEvent 開始事件從下到上回歸執(zhí)行每個控件的 onTouchEvent -------> return false;
注: 由于 View 沒有子 View 所以不需要 onInterceptTouchEvent 來控件是否把事件傳遞給子 View 還是攔截杂彭,所以 View 的事件分發(fā)調(diào)用 super.dispatchTouchEvent() 的時候默認(rèn)把事件傳給自己的 onTouchEvent 處理(相當(dāng)于攔截)墓毒,對比 ViewGroup 的 dispatchTouchEvent 事件分發(fā),View 的事件分發(fā)沒有上面提到的4個目標(biāo)的第3點(diǎn)
ViewGroup 和 View 的 onTouchEvent 方法是做事件處理的亲怠,那么這個事件只能有兩個處理方式:
1所计、自己消費(fèi)掉,事件終結(jié)团秽,不再傳給誰----->return true;
2主胧、繼續(xù)從下往上傳,不消費(fèi)事件习勤,讓父View也能收到到這個事件----->return false;View的默認(rèn)實(shí)現(xiàn)是不消費(fèi)的踪栋。所以super==false。
ViewGroup的onInterceptTouchEvent方法對于事件有兩種情況:
1图毕、攔截下來夷都,給自己的onTouchEvent處理--->return true;
2、不攔截予颤,把事件往下傳給子View---->return false,ViewGroup默認(rèn)是不攔截的损肛,所以super==false;
關(guān)于ACTION_MOVE 和 ACTION_UP
上面講解的都是針對ACTION_DOWN的事件傳遞荣瑟,ACTION_MOVE和ACTION_UP在傳遞的過程中并不是和ACTION_DOWN 一樣治拿,你在執(zhí)行ACTION_DOWN的時候返回了false,后面一系列其它的action就不會再得到執(zhí)行了笆焰。簡單的說劫谅,就是當(dāng)dispatchTouchEvent在進(jìn)行事件分發(fā)的時候,只有前一個事件(如ACTION_DOWN)返回true,才會收到ACTION_MOVE和ACTION_UP的事件捏检。具體這句話很多博客都說了荞驴,但是具體含義是什么呢?我們來看一下下面的具體分析贯城。
上面提到過了熊楼,事件如果不被打斷的話是會不斷往下傳到葉子層(View),然后又不斷回傳到Activity能犯,dispatchTouchEvent 和 onTouchEvent 可以通過return true 消費(fèi)事件鲫骗,終結(jié)事件傳遞,而onInterceptTouchEvent 并不能消費(fèi)事件踩晶,它相當(dāng)于是一個分叉口起到分流導(dǎo)流的作用执泰,ACTION_MOVE和ACTION_UP 會在哪些函數(shù)被調(diào)用,之前說了并不是哪個函數(shù)收到了ACTION_DOWN渡蜻,就會收到 ACTION_MOVE 等后續(xù)的事件的术吝。
下面通過幾張圖看看不同場景下,ACTION_MOVE事件和ACTION_UP事件的具體走向并總結(jié)一下規(guī)律茸苇。
//打印日志Activity|dispatchTouchEvent-->ACTION_DOWN ViewGroup1|dispatchTouchEvent-->ACTION_DOWN---->消費(fèi)
在這種場景下ACTION_MOVE和ACTION_UP 將如何呢排苍,看下面的打出來的日志
Activity|dispatchTouchEvent-->ACTION_MOVE ViewGroup1|dispatchTouchEvent-->ACTION_MOVE----Activity|dispatchTouchEvent-->ACTION_UP ViewGroup1|dispatchTouchEvent-->ACTION_UP----
下圖中
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
2、我們在ViewGroup2 的dispatchTouchEvent 返回true消費(fèi)這次事件
Activity|dispatchTouchEvent-->ACTION_DOWN ViewGroup1|dispatchTouchEvent-->ACTION_DOWNViewGroup1|onInterceptTouchEvent-->ACTION_DOWNViewGroup2|dispatchTouchEvent-->ACTION_DOWN---->消費(fèi)Activity|dispatchTouchEvent-->ACTION_MOVE ViewGroup1|dispatchTouchEvent-->ACTION_MOVEViewGroup1|onInterceptTouchEvent-->ACTION_MOVEViewGroup2|dispatchTouchEvent-->ACTION_MOVE----TouchEventActivity|dispatchTouchEvent-->ACTION_UP ViewGroup1|dispatchTouchEvent-->ACTION_UPViewGroup1|onInterceptTouchEvent-->ACTION_UPViewGroup2|dispatchTouchEvent-->ACTION_UP----
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
3学密、我們在View 的 dispatchTouchEvent 返回 true 消費(fèi)這次事件
這個我不就畫圖了淘衙,效果和在ViewGroup2 的dispatchTouchEvent return true的差不多,同樣的收到ACTION_DOWN 的dispatchTouchEvent函數(shù)都能收到 ACTION_MOVE和ACTION_UP则果。
所以我們就基本可以得出結(jié)論如果在某個控件的dispatchTouchEvent 返回true消費(fèi)終結(jié)事件幔翰,那么收到ACTION_DOWN 的函數(shù)也能收到 ACTION_MOVE和ACTION_UP。
4西壮、我們在View 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
5遗增、我們在ViewGroup 2 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
6、我們在ViewGroup 1 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
7款青、我們在Activity 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
8做修、我們在View的dispatchTouchEvent 返回false并且Activity 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
9、我們在View的dispatchTouchEvent 返回false并且ViewGroup 1 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
10抡草、我們在View的dispatchTouchEvent 返回false并且在ViewGroup 2 的onTouchEvent 返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
11饰及、我們在ViewGroup2的dispatchTouchEvent 返回false并且在ViewGroup1 的onTouchEvent返回true消費(fèi)這次事件
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
12、我們在 ViewGroup2 的 onInterceptTouchEvent 返回 true 攔截此次事件并且在ViewGroup 1 的onTouchEvent返回true消費(fèi)這次事件康震。
紅色的箭頭代表ACTION_DOWN 事件的流向
藍(lán)色的箭頭代表ACTION_MOVE 和 ACTION_UP 事件的流向
對于在onTouchEvent消費(fèi)事件的情況:在哪個View的onTouchEvent 返回true燎含,那么ACTION_MOVE和ACTION_UP的事件從上往下傳到這個View后就不再往下傳遞了,而直接傳給自己的onTouchEvent 并結(jié)束本次事件傳遞過程腿短。
對于ACTION_MOVE屏箍、ACTION_UP總結(jié):ACTION_DOWN事件在哪個控件消費(fèi)了(return true)绘梦, 那么ACTION_MOVE和ACTION_UP就會從上往下(通過dispatchTouchEvent)做事件分發(fā)往下傳,就只會傳到這個控件赴魁,不會繼續(xù)往下傳卸奉,如果ACTION_DOWN事件是在 dispatchTouchEvent 消費(fèi),那么事件到此為止停止傳遞(MOVE 和 UP也會傳到這里)颖御,如果ACTION_DOWN事件是在 onTouchEvent 消費(fèi)的榄棵,那么會把ACTION_MOVE或ACTION_UP事件傳給該控件的 onTouchEvent 處理并結(jié)束傳遞。
作者:阿瑞921
鏈接:http://www.reibang.com/p/11b37cfebb21
來源:簡書
著作權(quán)歸作者所有潘拱。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)疹鳄,非商業(yè)轉(zhuǎn)載請注明出處。