最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

app開發(fā)觸摸事件的ACTION_CANCEL怎么產(chǎn)生-討厭的android觸摸面試題

2023-09-09 16:40 作者:千里馬學(xué)框架  | 我要投稿

hi,粉絲朋友!?

大家對于MotionEvent.ACTION_CANCEL這個(gè)cancel事件是不是感覺又熟悉又陌生,熟悉是因?yàn)榻?jīng)常在onTouch識別觸摸事件時(shí)候會把它和ACTION_UP放在一塊處理,基本停留在字面意思理解為 “”取消“”

對應(yīng)的文章視頻講解:https://www.bilibili.com/video/BV1Ti4y1D7Pq/


更多android 干貨知識分享請+W:androidframework007


ACTION_CANCEL觸發(fā)場景和原因:

customTextView.setOnTouchListener(new?View.OnTouchListener()?{
????@Override
????public?boolean?onTouch(View?view,?MotionEvent?motionEvent)?{
????????Log.i("test2","?onTouch?motionEvent?=?"?+?motionEvent);
????????if?(motionEvent.getAction()?==?MotionEvent.ACTION_CANCEL?||?motionEvent.getAction()?==?MotionEvent.ACTION_UP?)?{
????????????Log.i("test2","?onTouch?ACTION_CANCEL?=?"?,new?Exception());
????????}
????????return?true;
????}
});

所以這里本節(jié)課就來帶大家深入理解這里ACTION_CANCEL

首先我們來分析應(yīng)用觸摸最常見的ACTION_CANCEL情況:

在這里插入圖片描述

這個(gè)就是我們常見的一個(gè)父布局MyLayout,它裝載了2個(gè)子控件textview1和textview2.假設(shè)一個(gè)觸摸事件到來,我們知道觸摸事件的傳遞順序就是:父布局 ?-- 》 子布局 這樣的一個(gè)順序,也就是其實(shí)事件父布局是具有完全的決策權(quán)利來是否給子布局,可以通過方法onInterceptTouchEvent。 接下來我們又來看看MotionEvent,一個(gè)正常完整觸摸應(yīng)該是怎么個(gè)順序呢? ACTION_DOWN -- > ACTION_MOVE --> ACTION_UP 所以說我們控件正常事件處理就是最少要有DOWN --》UP兩個(gè)事件,才代表事件介紹,那么你會問如果只有DOWN,和MOVE是否可以呢?答案:當(dāng)然是不可以的 為啥呢? 大家可以想一下這樣一個(gè)場景,你是一個(gè)按鈕,來了觸摸事件DOWN了后,你把按鈕變成selected狀態(tài)了,你本來一直等值來個(gè)UP事件來變回正常狀態(tài),如果系統(tǒng)發(fā)生依次你沒有收到UP會怎么樣呢?那就是你的應(yīng)用按鈕就永遠(yuǎn)處于選中狀態(tài)無法取消,一直到進(jìn)程關(guān)閉(當(dāng)然說是正常你沒有特別處理情況)。

所以有了以上基礎(chǔ)后,大家就知道有了DOWN事件一般都要有UP事件才算完整,但是有一些場景他就是可能收到了DOWN,后面收不到UP了怎么辦? 比如看如下例子:

????@Override
????public?boolean?onInterceptTouchEvent(MotionEvent?ev)?{
????????Log.i("test2","onInterceptTouchEvent?ev?=?"?+?ev);
????????if?(ev.getAction()?==?MotionEvent.ACTION_MOVE?)?{//我們不攔截DOWN事件,但是攔截MOVE事件,父布局一攔截,子控件就會收到ACTION_CANCEL
????????????return?true;
????????}
????????return?super.onInterceptTouchEvent(ev);
????}

這種情況我們父布局只并沒有攔截DOWN事件,所以DOWN傳遞給你textview1,但是因?yàn)樵贛OVE時(shí)候我們父布局?jǐn)r截了,即說明事件不再傳遞給子控件了textview1,那么這個(gè)是不是就是textview1收到了DOWN,但是收不到UP情況,不完整了,那該怎么辦?

這個(gè)時(shí)候其實(shí)就是今天重點(diǎn)介紹的ACTION_CANCEL出廠,他就是來幫忙解決上面的因?yàn)楦覆季种虚g進(jìn)行了觸摸事件攔截,但是子布局又要一個(gè)完整觸摸過程,那么就需要傳遞一個(gè)觸摸事件給子控件,那么傳遞什么合適? 大家肯定會想U(xiǎn)P最合適,UP不就可以了?但是你要想想用戶還沒有UP???如果傳遞了UP是不是也可能會有問題,所以這時(shí)候就是傳遞ACTION_CANCEL,它代表是取消,即代表這個(gè)觸摸事件到此被取消結(jié)束了??丶盏叫枰约鹤鰧?yīng)的掃尾工作,保證事件完整。

上面我們已經(jīng)對ACTION_CANCEL觸發(fā)場景和原因已經(jīng)清楚,接下來看看源碼是怎么處理的:

ACTION_CANCEL出現(xiàn)源碼分析

app代碼: layout.xml

?????android:layout_width="match_parent"
????????android:layout_height="wrap_content"
????????android:orientation="vertical"
????????android:paddingTop="100dp"
????????>
????????<TextView
????????????android:id="@+id/custom_text"
????????????android:layout_width="wrap_content"
????????????android:layout_height="wrap_content"
????????????android:text="Hello?CustomLayout?!"
????????????>
</TextView>

????</com.example.anrdemo.CustomLayout>

代碼端對onTouch的事件進(jìn)行監(jiān)聽:

???TextView?customTextView?=?(TextView)findViewById(R.id.custom_text);
????????customTextView.setOnTouchListener(new?View.OnTouchListener()?{
????????????@Override
????????????public?boolean?onTouch(View?view,?MotionEvent?motionEvent)?{
????????????????Log.i("test2","?onTouch?motionEvent?=?"?+?motionEvent);
????????????????if?(motionEvent.getAction()?==?MotionEvent.ACTION_CANCEL?||?motionEvent.getAction()?==?MotionEvent.ACTION_UP?)?{
????????????????//變成了CANCEL后打印一下堆棧,追蹤框架哪里進(jìn)行了改變
????????????????????Log.i("test2","?onTouch?ACTION_CANCEL?=?"?,new?Exception());
????????????????}
????????????????return?true;
????????????}
????????});
2021-11-27?13:50:06.911?7010-7010/com.example.anrdemo?I/test2:??onTouch?ACTION_CANCEL?=?
????java.lang.Exception
????????at?com.example.anrdemo.MainActivity$2.onTouch(MainActivity.java:46)
????????at?android.view.View.dispatchTouchEvent(View.java:13949)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
????????at?android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
????????at?com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)
????????at?com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)
????????at?android.app.Activity.dispatchTouchEvent(Activity.java:4011)
????????at?com.example.anrdemo.MainActivity.dispatchTouchEvent(MainActivity.java:72)
????????at?androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
????????at?androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
????????at?com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:423)
????????at?android.view.View.dispatchPointerEvent(View.java:14212)
????????at?android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5652)
????????at?android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5455)
????????at?android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4958)
????????at?android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5011)
????????at?android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4977)
????????at?android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5117)
????????at?android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4985)
????????at?android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5174)
????????at?android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4958)
????????at?android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5011)
????????at?android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4977)
????????at?android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4985)
????????at?android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4958)
????????at?android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7675)
????????at?android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7644)
????????at?android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7605)
????????at?android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7800)
????????at?android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:188)
????????at?android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native?Method)
????????at?android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:178)
????????at?android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7751)
????????at?android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7824)
????????at?android.view.Choreographer$CallbackRecord.run(Choreographer.java:967)
????????at?android.view.Choreographer.doCallbacks(Choreographer.java:791)
????????at?android.view.Choreographer.doFrame(Choreographer.java:719)
????????at?android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
????????at?android.os.Handler.handleCallback(Handler.java:883)
????????at?android.os.Handler.dispatchMessage(Handler.java:100)

這里就打印出來了框架調(diào)用到onTouch中MotionEvent變成CANCEL的流程,方便我們進(jìn)行源碼分析。 具體大家可以自己取追蹤,這里我這邊列出結(jié)果: base/core/java/android/view/ViewGroup.java


????????????//?Check?for?interception.
????????????final?boolean?intercepted;
????????????if?(actionMasked?==?MotionEvent.ACTION_DOWN
????????????????????||?mFirstTouchTarget?!=?null)?{
????????????????final?boolean?disallowIntercept?=?(mGroupFlags?&?FLAG_DISALLOW_INTERCEPT)?!=?0;
????????????????if?(!disallowIntercept)?{
????????????????????intercepted?=?onInterceptTouchEvent(ev);
????????????????????ev.setAction(action);?//?restore?action?in?case?it?was?changed
????????????????}?else?{
????????????????????intercepted?=?false;
????????????????}
????????????}?

這里onInterceptTouchEvent會調(diào)用到我們CustomLayout的onInterceptTouchEvent,我們識別到了如果MOVE就return true,所以intercepted這個(gè)時(shí)候也是true。

final?boolean?cancelChild?=?resetCancelNextUpFlag(target.child)
????????????????????????????????||?intercepted;
????????????????????????if?(dispatchTransformedTouchEvent(ev,?cancelChild,
????????????????????????????????target.child,?target.pointerIdBits))

這里cancelChild就變成了true,所以

private?boolean?dispatchTransformedTouchEvent(MotionEvent?event,?boolean?cancel,
????????????View?child,?int?desiredPointerIdBits)
?{
????????final?boolean?handled;

????????final?int?oldAction?=?event.getAction();
????????if?(cancel?||?oldAction?==?MotionEvent.ACTION_CANCEL)?{//根據(jù)前面cancelChild變成了CANCEL
????????????event.setAction(MotionEvent.ACTION_CANCEL);
????????????if?(child?==?null)?{
????????????????handled?=?super.dispatchTouchEvent(event);
????????????}?else?{
????????????????handled?=?child.dispatchTouchEvent(event);
????????????}
????????????event.setAction(oldAction);
????????????return?handled;
????????}
//---省略部分
}
????????
從以上可以看出,結(jié)合我們Input專題學(xué)習(xí)知識,我們知道觸摸事件InputDispatcher傳遞給App實(shí)際還是ACTION_MOVE,但是app進(jìn)程ViewGroup的策略把事件變成了ACTION_CANCEL來保證事件的完整性。 總結(jié)流程圖:

在這里插入圖片描述




app開發(fā)觸摸事件的ACTION_CANCEL怎么產(chǎn)生-討厭的android觸摸面試題的評論 (共 條)

分享到微博請遵守國家法律
离岛区| 北碚区| 韶山市| 海原县| 嫩江县| 枣庄市| 渭南市| 南靖县| 沙湾县| 沛县| 青州市| 沧州市| 卓资县| 兴和县| 光泽县| 灵山县| 馆陶县| 七台河市| 广河县| 额济纳旗| 平利县| 乌恰县| 新余市| 九龙坡区| 香格里拉县| 嘉祥县| 项城市| 佛坪县| 平陆县| 沈丘县| 广平县| 泗阳县| 邓州市| 开鲁县| 楚雄市| 巴马| 新竹市| 泰顺县| 安新县| 泽州县| 渝北区|