日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

如何自己實現(xiàn)Android View Touch事件分發(fā)流程

瀏覽:19日期:2022-09-20 11:09:40

Android Touch事件分發(fā)是Android UI中的重要內(nèi)容,Touch事件從驅(qū)動層向上,經(jīng)過InputManagerService,WindowManagerService,ViewRootImpl,Window,到達DecorView,經(jīng)View樹分發(fā),最終被消費。

本文嘗試通過對其中View部分的事件分發(fā),也是與日常開發(fā)聯(lián)系最緊密的部分,進行重寫。說是重寫,其實是對Android該部分源碼進行大幅精簡而不失要點,且能夠獨立運行,以一窺其全貌,而不陷入到源碼繁雜的細節(jié)中。

以下類均為自定義類,而非Android同名原生類。

MotionEvent

class MotionEvent { companion object { const val ACTION_DOWN = 0 const val ACTION_MOVE = 1 const val ACTION_UP = 2 const val ACTION_CANCEL = 3 } var x = 0 var y = 0 var action = 0 override fun toString(): String { return 'MotionEvent(x=$x, y=$y, action=$action)' }}

首先定義MotionEvent,這里將觸摸事件action減少為最常用的4種,同時只支持單指操作,因此action取值僅支持4個常量。并且為了簡化后續(xù)的位置計算,x和y表示的是絕對坐標(相當于getRawX()與getRawY()),而非相對坐標。

View

open class View { var left = 0 var right = 0 var top = 0 var bottom = 0//1 var enable = true var clickable = false var onTouch: ((View, MotionEvent) -> Boolean)? = null var onClick: ((View) -> Unit)? = null//3 set(value) { field = value clickable = true } private var downed = false open fun layout(l: Int, t: Int, r: Int, b: Int) { left = l top = t right = r bottom = b }//2 open fun onTouchEvent(ev: MotionEvent): Boolean { var handled: Boolean if (enable && clickable) { when (ev.action) { MotionEvent.ACTION_DOWN -> { downed = true } MotionEvent.ACTION_UP -> { if (downed && ev.inView(this)) {//7 downed = false onClick?.invoke(this) } } MotionEvent.ACTION_MOVE -> { if (!ev.inView(this)) {//7 downed = false } } MotionEvent.ACTION_CANCEL -> { downed = false } } handled = true } else { handled = false } return handled }//5 open fun dispatchTouchEvent(ev: MotionEvent): Boolean { var result = false if (onTouch != null && enable) { result = onTouch!!.invoke(this, ev) } if (!result && onTouchEvent(ev)) { result = true } return result }//4}fun MotionEvent.inView(v: View) = v.left <= x && x <= v.right && v.top <= y && y <= v.bottom//6

接下來定義View。(1)定義了View的位置,這里同樣表示絕對坐標,而不是相對于父View的位置。(2)同時使用layout方法傳遞位置,因為我們的重點是View的事件分發(fā)而不是其布局與繪制,因此只定義了layout。(3)觸摸回調(diào)這里直接使用函數(shù)類型定義,(4)dispatchTouchEvent先處理了onTouch回調(diào),如果未回調(diào),則調(diào)用onTouchEvent,可見二者的優(yōu)先級。(5)onTouchEvent則主要處理了onClick回調(diào),雖然真實源碼中對點擊的判斷更為復雜,但實際效果與此處是一致的,(6)使用擴展函數(shù)來確定事件是否發(fā)生在View內(nèi)部,(7)兩處調(diào)用配合downed標記確保ACTION_MOVE與ACTION_UP發(fā)生在View內(nèi)才被識別為點擊。至于長按等其他手勢的監(jiān)聽,因為較為繁瑣,這里就不再實現(xiàn)。

ViewGroup

open class ViewGroup(private vararg val children: View) : View() {//1 private var mFirstTouchTarget: View? = null open fun onInterceptTouchEvent(ev: MotionEvent): Boolean { return false }//2 override fun dispatchTouchEvent(ev: MotionEvent): Boolean {//3 val intercepted: Boolean var handled = false if (ev.action == MotionEvent.ACTION_DOWN) { mFirstTouchTarget = null }//4 if (ev.action == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { intercepted = onInterceptTouchEvent(ev)//5 } else { intercepted = true//6 } val canceled = ev.action == MotionEvent.ACTION_CANCEL var alreadyDispatchedToNewTouchTarget = false if (!intercepted) { if (ev.action == MotionEvent.ACTION_DOWN) {//7 for (child in children.reversed()) {//8 if (ev.inView(child)) {//9 if (dispatchTransformedTouchEvent(ev, false, child)) {//10 mFirstTouchTarget = child alreadyDispatchedToNewTouchTarget = true//12 } break } } } } if (mFirstTouchTarget == null) { handled = dispatchTransformedTouchEvent(ev, canceled, null)//17 } else { if (alreadyDispatchedToNewTouchTarget) {//13 handled = true } else { val cancelChild = canceled || intercepted//14 if (dispatchTransformedTouchEvent(ev, cancelChild, mFirstTouchTarget)) { handled = true } if (cancelChild) { mFirstTouchTarget = null//16 } } } if (canceled || ev.action == MotionEvent.ACTION_UP) { mFirstTouchTarget = null }//4 return handled } private fun dispatchTransformedTouchEvent(ev: MotionEvent, cancel: Boolean, child: View?): Boolean { if (cancel) { ev.action = MotionEvent.ACTION_CANCEL//15 } val oldAction = ev.action val handled = if (child == null) { super.dispatchTouchEvent(ev)//18 } else { child.dispatchTouchEvent(ev)//11 } ev.action = oldAction return handled }}

最后來實現(xiàn)ViewGroup:(1)子View這里通過構造函數(shù)傳入, 而不再提供addView等方法,(2)onInterceptTouchEvent簡單返回false,主要通過子類繼承來修改返回,(3)dispatchTouchEvent是整個實現(xiàn)中最主要的邏輯,來詳細解釋,這里的實現(xiàn)只包含對單指Touch事件的處理,并且不包含requestDisallowInterceptTouchEvent的情況。

(4)源碼中開頭和結尾處有清理字段與標記的方法,用于在一個事件序列(由ACTION_DOWN開始,經(jīng)過若干ACTION_MOVE等,最終以ACTION_UP結束,即整個觸摸過程)開頭和結束時清理舊數(shù)據(jù),這里簡化為了將我們類中的唯一字段mFirstTouchTarget(表示整個事件序列的目標視圖,在源碼中,此變量類型為TouchTarget,實現(xiàn)為一個View的鏈表節(jié)點,以此來支持多指觸摸,這里簡化為View)置空。

接下來將該方法分為幾部分來介紹:

事件攔截

(5)表示在一個事件序列的開始或者已經(jīng)找到了目標視圖的情況下,才需要調(diào)用onInterceptTouchEvent判斷本ViewGroup是否攔截事件。(6)表示如果ACTION_DOWN沒有視圖消費,則之后的事件將被攔截,且攔截的View是View樹中的頂層View,即Android中的DecorView。

尋找目標視圖,分發(fā)ACTION_DOWN

(7)當ACTION_DOWN事件未被攔截,(8)則反向遍歷子View數(shù)組,(9)尋找ACTION_DOWN事件落在其中的View,(10)并將ACTION_DOWN事件傳遞給該子View,這一步調(diào)用了dispatchTransformedTouchEvent,該方法將源碼中的方法簡化為了三參數(shù),方法名中的Transformed表示,會將Touch事件進行坐標系的變換,而這里為了簡化使用的坐標是絕對的,因此不需要變換。此時會調(diào)用dispatchTransformedTouchEvent中(11)處向子View分發(fā)ACTION_DOWN,child即mFirstTouchTarget。

分發(fā)除ACTION_DOWN外的其他事件

(12)對于ACTION_DOWN事件,會將alreadyDispatchedToNewTouchTarget置位,(13)此時會會進入if塊,而非ACTION_DOWN事件會進入else塊。(14)當該事件是ACTION_CANCEL或者事件被攔截,則在調(diào)用dispatchTransformedTouchEvent的(15)處后,將事件修改為ACTION_CANCEL,然后調(diào)用(11),將ACTION_CANCEL分發(fā)給子View,(16)同時將mFirstTouchTarget置空。當事件序列中的下個事件到來時,會進入(17)處,即最終調(diào)用(18),調(diào)用上節(jié)中View的事件處理,即ViewGroup消費該事件,消費該事件的ViewGroup即攔截了非ACTION_DOWN事件并向子View分發(fā)ACTION_CANCEL的ViewGroup。

使用

至此,實現(xiàn)了MotionEvent,View,與ViewGroup,來進行一下驗證。

定義三個子類:

class VG1(vararg children: View) : ViewGroup(*children)class VG2(vararg children: View) : ViewGroup(*children)class V : View() { override fun onTouchEvent(ev: MotionEvent): Boolean { println('V onTouchEvent $ev') return super.onTouchEvent(ev) } override fun dispatchTouchEvent(ev: MotionEvent): Boolean { println('V dispatchTouchEvent $ev') return super.dispatchTouchEvent(ev) }}

定義一個事件發(fā)生方法,由該方法來模擬Touch事件的軌跡與action:

fun produceEvents(startX: Int, startY: Int, endX: Int, endY: Int, stepNum: Int): List<MotionEvent> { val list = arrayListOf<MotionEvent>() val stepX = (endX - startX) / stepNum val stepY = (endY - startY) / stepNum for (i in 0..stepNum) { when (i) { 0 -> { list.add(MotionEvent().apply { action = MotionEvent.ACTION_DOWN x = startX y = startY }) } stepNum -> { list.add(MotionEvent().apply { action = MotionEvent.ACTION_UP x = endX y = endY }) } else -> { list.add(MotionEvent().apply { action = MotionEvent.ACTION_MOVE x = stepX * i + startX y = stepY * i + startY }) } } } return list}

接下來就可以驗證了,在Android中事件由驅(qū)動層一步步傳遞至View樹的頂端,這里我們定義一個三層的布局page,(1)直接將事件序列遍歷調(diào)用頂層ViewGroup的dispatchTouchEvent來開啟事件分發(fā)。

fun main() { val page = VG1( VG2( V().apply { layout(0, 0, 100, 100); onClick = { println('Click in V') } }//2 ).apply { layout(0, 0, 200, 200) } ).apply { layout(0, 0, 300, 300) }//3 val events = produceEvents(50, 50, 90, 90, 5) events.forEach { page.dispatchTouchEvent(it)//1 }}

程序可以正常執(zhí)行,打印如下:

V dispatchTouchEvent MotionEvent(x=50, y=50, action=0)V onTouchEvent MotionEvent(x=50, y=50, action=0)V dispatchTouchEvent MotionEvent(x=58, y=58, action=1)V onTouchEvent MotionEvent(x=58, y=58, action=1)V dispatchTouchEvent MotionEvent(x=66, y=66, action=1)V onTouchEvent MotionEvent(x=66, y=66, action=1)V dispatchTouchEvent MotionEvent(x=74, y=74, action=1)V onTouchEvent MotionEvent(x=74, y=74, action=1)V dispatchTouchEvent MotionEvent(x=82, y=82, action=1)V onTouchEvent MotionEvent(x=82, y=82, action=1)V dispatchTouchEvent MotionEvent(x=90, y=90, action=2)V onTouchEvent MotionEvent(x=90, y=90, action=2)Click in V

因為我們在(2)增加了點擊事件,以上表示了一次點擊的事件分發(fā)。也可以重寫修改page布局(3)來查看其它情景下的事件分發(fā)流程,或者重寫VG1,VG2的方法,增加打印并查看。

總結

通過對Android 源碼的整理,用約150行代碼就能實現(xiàn)了一個簡化版的Android Touch View事件分發(fā),雖然為了代碼結構的簡潔舍棄了部分功能,但整個流程與Android Touch View事件分發(fā)是一致的,能夠更方便理解這套機制。

以上就是如何自己實現(xiàn)Android View Touch事件分發(fā)流程的詳細內(nèi)容,更多關于實現(xiàn)Android View Touch事件分發(fā)流程的資料請關注好吧啦網(wǎng)其它相關文章!

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
四虎884aa成人精品最新| 免费观看久久av| 奶水喷射视频一区| 999视频精品| 久久精品欧美一区| 久久久精品日韩| 高清av一区| 精品久久免费| 成人美女视频| 99成人在线视频| 婷婷成人基地| 亚洲一区激情| 亚洲字幕久久| 亚洲一区二区av| 日韩视频1区| 久久精品99国产精品日本| 国产精品乱战久久久| 欧美激情视频一区二区三区免费| 国产黄色精品| 高清一区二区三区av| 日韩一区二区中文| 日韩大片在线观看| 色老板在线视频一区二区| 99成人在线视频| 日韩午夜电影| 亚洲在线久久| 国产欧美一级| 国产福利91精品一区二区| 日韩久久一区二区三区| 亚洲福利久久| 亚洲色图网站| 国产一精品一av一免费爽爽| 麻豆国产精品视频| 欧美日韩精品免费观看视欧美高清免费大片 | 欧美日一区二区| 欧美特黄一区| 日韩成人在线看| 国产成人久久| 亚洲精品在线观看91| 亚洲区欧美区| 国产精品一级| 日韩精品1区| 亚洲一区欧美二区| 国产欧美日韩一级| 成人日韩在线观看| 91久久久久| 国产精品成人3p一区二区三区| 精品无人区麻豆乱码久久久| 日韩三区免费| 亚洲精品国产精品粉嫩| 久久伊人国产| 亚洲色诱最新| 久久久久久亚洲精品美女| 精品一区三区| 日韩av不卡一区二区| 日韩国产欧美一区二区| 免费高清在线一区| 精品免费av一区二区三区| 最新亚洲激情| 精品欠久久久中文字幕加勒比| 亚洲美洲欧洲综合国产一区| 国产免费播放一区二区| 日韩在线精品| 亚洲18在线| 日韩精品麻豆| 国产女人18毛片水真多18精品| 久久精品二区三区| 久久精品xxxxx| 亚洲精品888| 精品亚洲成人| 日韩中文av| 久久香蕉国产| 老司机免费视频一区二区三区| 亚洲自啪免费| 天堂√中文最新版在线| 青青青国产精品| 婷婷亚洲五月| 精品国产欧美日韩| 亚洲色图综合| 在线成人动漫av| 麻豆国产欧美日韩综合精品二区| 欧美日韩国产探花| 久久电影tv| 国产亚洲欧美日韩在线观看一区二区 | 美女视频免费精品| 亚洲一区观看| 91精品在线观看国产| 久久不见久久见免费视频7| 久久午夜影视| 美女福利一区二区三区| 国产精品九九| 日本不卡视频在线观看| 午夜av一区| 超级白嫩亚洲国产第一| 国产乱码精品一区二区亚洲| 蜜臀av亚洲一区中文字幕| 激情久久久久久| 福利一区和二区| 国产欧美日韩视频在线 | 日韩欧美中文字幕电影| 欧美丝袜一区| 国产白浆在线免费观看| 欧美精品导航| 久久精品99国产精品| 亚洲91在线| 在线精品福利| 噜噜噜躁狠狠躁狠狠精品视频| 久久中文字幕av| 欧美久久天堂| 日产午夜精品一线二线三线| 国产欧美欧美| 国产毛片久久久| 欧美日韩1区2区3区| 亚洲男人在线| 亚洲a级精品| 亚洲精品自拍| 石原莉奈一区二区三区在线观看| 欧美亚洲国产一区| 久久影院一区| 欧洲激情综合| 女人天堂亚洲aⅴ在线观看| 久久激情中文| 激情久久久久久| 免费观看久久av| 国产高清久久| 在线综合亚洲| 蜜臀av在线播放一区二区三区| 亚洲激情另类| 在线精品亚洲| 日韩超碰人人爽人人做人人添| 日本不卡一区二区| 一区二区电影在线观看| 日韩专区欧美专区| 热久久免费视频| 蜜桃视频一区二区三区在线观看| 亚洲欧美日韩国产综合精品二区| 国产麻豆综合| 亚洲ab电影| 97se亚洲| 久久免费福利| 丝袜诱惑一区二区| 欧美日韩中文字幕一区二区三区 | 狠狠色综合网| 视频一区二区三区入口| 女人天堂亚洲aⅴ在线观看| 午夜亚洲福利在线老司机| 美美哒免费高清在线观看视频一区二区| 美女精品在线观看| 亚洲毛片一区| 国产欧美午夜| 捆绑调教日本一区二区三区| 国产99久久久国产精品成人免费| 精品一区在线| 亚洲一二av| 国产日韩欧美一区二区三区| 精品中文字幕一区二区三区| 日韩一区二区三区免费播放| 亚洲激情社区| 日韩区一区二| 麻豆中文一区二区| 国产麻豆久久| 鲁大师影院一区二区三区| 日韩精品一级| 国产aa精品| 国产精品一国产精品| www.com.cn成人| 午夜在线播放视频欧美| 欧美天堂一区二区| 国产精品福利在线观看播放| 欧美日韩三区| 欧美精品中文| 天堂av在线| 日韩精品一级中文字幕精品视频免费观看| 亚州av日韩av| 精品三级av在线导航| 99视频精品免费观看| 久久国内精品视频| 精品三级久久| 亚洲精品一二三**| 国产精品二区不卡| 日韩中文字幕一区二区三区| 老色鬼精品视频在线观看播放| 久久久久久久久久久9不雅视频| 免费人成在线不卡| 精品伊人久久| 丝袜国产日韩另类美女| 麻豆国产91在线播放| 日韩午夜av在线| 免费一区二区三区在线视频| 久久精品国语| 久久精品99国产精品日本| 久久免费高清| 日韩av字幕| 99成人超碰| 国产另类在线| 欧美日韩视频| 精品久久电影| 中文字幕一区日韩精品| 亚洲日本网址| 91伊人久久|