安卓自定義view

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)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泽铛,一起剝皮案震驚了整個濱河市尚辑,隨后出現(xiàn)的幾起案子辑鲤,更是在濱河造成了極大的恐慌盔腔,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件月褥,死亡現(xiàn)場離奇詭異弛随,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宁赤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門舀透,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人决左,你說我怎么就攤上這事愕够。” “怎么了佛猛?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵惑芭,是天一觀的道長。 經(jīng)常有香客問我继找,道長遂跟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任婴渡,我火速辦了婚禮幻锁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘边臼。我一直安慰自己哄尔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布柠并。 她就那樣靜靜地躺著岭接,像睡著了一般置谦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亿傅,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天媒峡,我揣著相機(jī)與錄音,去河邊找鬼葵擎。 笑死谅阿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酬滤。 我是一名探鬼主播签餐,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼盯串!你這毒婦竟也來了氯檐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤体捏,失蹤者是張志新(化名)和其女友劉穎冠摄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體几缭,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡河泳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了年栓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拆挥。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖某抓,靈堂內(nèi)的尸體忽然破棺而出纸兔,到底是詐尸還是另有隱情,我是刑警寧澤否副,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布汉矿,位于F島的核電站,受9級特大地震影響副编,放射性物質(zhì)發(fā)生泄漏负甸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一痹届、第九天 我趴在偏房一處隱蔽的房頂上張望呻待。 院中可真熱鬧,春花似錦队腐、人聲如沸蚕捉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迫淹。三九已至秘通,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敛熬,已是汗流浹背肺稀。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留应民,地道東北人话原。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像诲锹,于是被迫代替她去往敵國和親繁仁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容