摘要:有經(jīng)驗(yàn)的App Inventor 愛好者希望能夠定制系統(tǒng),增加自己需要的功能。文章以 App Inventor中的畫布組件為例,分析安卓系統(tǒng)中觸控事件的處理機(jī)制,詳細(xì)介紹畫布組件多點(diǎn)觸控的實(shí)現(xiàn)方法,并實(shí)現(xiàn)了縮放手勢和旋轉(zhuǎn)手勢控制功能,為有意定制 App Inventor的愛好者提供參考。
關(guān)鍵詞:App Inventor;畫布;多點(diǎn)觸控;手勢控制
中圖分類號:TP319? ? ? 文獻(xiàn)標(biāo)識(shí)碼:A
文章編號:1009-3044(2022)17-0061-03
1 概述
App Inventor 系統(tǒng)簡單易學(xué),可視化的編程體驗(yàn)圈定了不少愛好者,他們經(jīng)過一段時(shí)間的使用后發(fā)現(xiàn),“簡單”既是App Inventor 的優(yōu)勢也是其制約。系統(tǒng)封裝后所提供的配置項(xiàng)很少,無法滿足個(gè)性化需要。App Inventor 基于Apache2 協(xié)議開源[1],是自由定制的必要前提,國內(nèi)外有很多不同的定制版。
安卓系統(tǒng)在2.0版本時(shí)引入多點(diǎn)觸控,在2.2版本重新設(shè)計(jì)后成為當(dāng)前使用的版本。然而 App Inventor 的畫布組件一直不支持多點(diǎn)觸控,在基于畫布組件制作游戲時(shí)頗為不便。國內(nèi)的 WxBit 圖形化編程系統(tǒng)在開源App Inventor的基礎(chǔ)上實(shí)現(xiàn)了完整的多點(diǎn)觸控支持,本文分析其中畫布組件實(shí)現(xiàn)多點(diǎn)觸控的方法。
2 分析設(shè)計(jì)
2.1 觸控事件的處理機(jī)制
在安卓系統(tǒng)中,用戶與APP的交互主要在Activity中完成。用戶觸碰屏幕時(shí),事件從 Activity到ViewGroup再到View,ViewGroup里面可以嵌套ViewGroup,是一個(gè)樹形結(jié)構(gòu),組件結(jié)構(gòu)示意圖如圖1所示。觸控事件產(chǎn)生后,逐級傳遞,直至被某個(gè)View消費(fèi),或者被某個(gè)ViewGroup攔截消費(fèi)。簡化的事件處理流程如圖2所示,在調(diào)用onTouchEvent之前還會(huì)檢查是否應(yīng)該調(diào)用注冊的onTouch事件監(jiān)聽[2],因未在畫布組件中使用,故省略。
2.2 畫布組件的事件處理機(jī)制
畫布(Canvas)是可見組件,繼承自基類 AndroidViewComponent,封裝的 CanvasView是自定義的View。CanvasView覆寫了onTouchEvent (MotionEvent[3-4] event)方法。從上一節(jié)的分析可以知道,觸控事件發(fā)生在畫布的范圍時(shí),就會(huì)進(jìn)入 onTouchEvent之中處理。為保證不被上層容器攔截,調(diào)用requestDisallowInterceptTouchEvent(true)設(shè)為不允許攔截。
在MotionEvent 中,存儲(chǔ)著觸控點(diǎn)的信息,每個(gè)觸控點(diǎn)都有PointerIndex(ActionIndex)和PointerId,PointerIndex會(huì)隨著觸控點(diǎn)的數(shù)量變化,而只要觸控點(diǎn)保持與屏幕接觸,PointerId就不會(huì)改變。在跟蹤觸控點(diǎn)時(shí),需要通過 PointerIndex獲得PointerId。觸控點(diǎn)對 PointerIndex和PointerId 的影響如表1所示。
3 系統(tǒng)實(shí)現(xiàn)
3.1 多點(diǎn)觸控
基于2.2節(jié)的分析可以知道,CanvasView 接收到的觸控事件是完整的,畫布組件未支持多點(diǎn)觸控的原因是原有的實(shí)現(xiàn)沒有做相對應(yīng)的處理。當(dāng)觸控點(diǎn)按下時(shí),CanvasView調(diào)用畫布的TouchDown事件;當(dāng)觸控點(diǎn)持續(xù)移動(dòng)時(shí),調(diào)用畫布的Dragged事件;當(dāng)觸控點(diǎn)松開時(shí),調(diào)用畫布的TouchUp事件,之后調(diào)用Touched事件。事件的處理時(shí)序如圖3所示。在其他組件中,也是一樣的處理流程。以水平布局和垂直布局增加多點(diǎn)觸控支持為例,將Canvas換成HVArrangement,CanvasView 換成HVArrangement中的ViewGroup ,調(diào)用ViewGroup的setOnTouchListener設(shè)置觸控事件的回調(diào)函數(shù)。
因?yàn)镻ointerId在接觸屏幕期間不變,可以用作區(qū)分觸控事件的標(biāo)識(shí),變更畫布的觸控事件定義增加參數(shù)pointerId,如圖4所示。
畫布的Dragged事件中需要用到起點(diǎn)和前點(diǎn)的坐標(biāo),在支持多點(diǎn)觸控的實(shí)現(xiàn)場景下,坐標(biāo)點(diǎn)信息保存在數(shù)組中,以PointerId為索引訪問。首先,將用于記錄坐標(biāo)信息的字段定義由單值改為數(shù)組,數(shù)組長度設(shè)置為MAX_FINGER常量,數(shù)組定義如圖5的A區(qū)代碼所示。然后,在onTouchEvent中取出觸控點(diǎn)編號,再根據(jù)編號取出PointerId。設(shè)備支持的觸控點(diǎn)數(shù)量與硬件有關(guān),不一定能達(dá)到最大值,為保證不出現(xiàn)數(shù)組越界異常,PointerId達(dá)到最大值時(shí)直接忽略,實(shí)現(xiàn)如圖5的B區(qū)代碼所示。
接下來就是根據(jù)觸控類型對事件進(jìn)行封裝,轉(zhuǎn)發(fā)回畫布組件中,調(diào)用圖4定義的代碼塊,具體實(shí)現(xiàn)如圖6所示。觸控點(diǎn)按下時(shí)進(jìn)入A區(qū),將坐標(biāo)點(diǎn)信息保存到數(shù)組,觸發(fā)畫布和所觸碰精靈組件的TouchDown事件;觸控點(diǎn)移動(dòng)時(shí)進(jìn)入B區(qū),當(dāng)移動(dòng)的距離小于閾值則忽略,否則觸發(fā)畫布和所觸碰精靈組件的Dragged事件;觸控點(diǎn)離開屏幕時(shí)進(jìn)入C區(qū),觸發(fā)所觸碰精靈組件的Touched和TouchUp事件,如果沒有觸發(fā)畫布的Dragged事件,則觸發(fā)Touched事件,最后觸發(fā)TouchUp事件。
需要注意的是在觸控點(diǎn)移動(dòng)時(shí),getActionIndex不能獲得觸控點(diǎn)的編號,需要遍歷每個(gè)觸摸點(diǎn),再調(diào)用event.getPointerId以獲得對應(yīng)的PointerId。
3.2 觸控手勢
多點(diǎn)觸控的重要應(yīng)用就是手勢控制,實(shí)現(xiàn)非常簡單。當(dāng)兩個(gè)觸控點(diǎn)靠近或遠(yuǎn)離時(shí),判斷為縮放手勢,常用于對圖片的放大和縮小。安卓SDK中提供了縮放手勢的檢測類,在重寫的方法中增加對畫布相應(yīng)代碼塊的調(diào)用即可,具體實(shí)現(xiàn)如圖7所示。
當(dāng)兩個(gè)觸控點(diǎn)圍繞某個(gè)中心點(diǎn)沿相反的方向移動(dòng),判斷為旋轉(zhuǎn)手勢,常用于對圖片的旋轉(zhuǎn)移動(dòng)等場景。使用的是 Almeros 開源的旋轉(zhuǎn)檢測項(xiàng)目[5],具體的實(shí)現(xiàn)類似縮放手勢,如圖8所示。
最后,在畫布組件的構(gòu)造函數(shù)中,將縮放手勢類和旋轉(zhuǎn)手勢類實(shí)例化,放進(jìn)手勢檢測集合。
4 結(jié)束語
本文分析的觸控事件的處理機(jī)制,在App Inventor系統(tǒng)中都適用。讀者如需親手實(shí)現(xiàn)畫布組件的多點(diǎn)觸控,先要熟悉App Inventor系統(tǒng)的源代碼結(jié)構(gòu)和編譯方法,詳見參考文獻(xiàn)[1]鏈接的README.md文件。其次,還需要對安卓應(yīng)用開發(fā)有一定的了解,能夠查閱SDK文檔。在某些組件(如按鈕及其子類)中注冊了onTouch事件監(jiān)聽,優(yōu)先于onTouchEvent運(yùn)行,為了讓事件能夠繼續(xù)傳遞,需要返回 false。盡管實(shí)現(xiàn)有些差異,原理與本文所描述并不沖突,故本文有較大的參考價(jià)值。
參考文獻(xiàn):
[1] MIT App Inventor Public Open Source[EB/OL]. [2021-10-26].https://github.com/mit-cml/appinventor-sources.
[2] Input events overview[EB/OL]. [2021-10-26].https://developer.android.com/guide/topics/ui/ui-events.
[3] MotionEvent[EB/OL].[2021-10-26].https://developer.android.com/reference/android/view/MotionEvent.
[4] POWELL A.Making Sense of Multitouch[EB/OL].[2021-10-26].https://developer.android.com/reference/android/view/MotionEvent.
[5] Gesture detector framework for multitouch handling on Android, based on Android's ScaleGestureDetector[EB/OL].[2021-10-26].https://github.com/Almeros/android-gesture-detectors.
收稿日期:2022-03-16
作者簡介:楊道全(1982—),男,廣東雷州人,碩士,研究方向?yàn)榛ヂ?lián)網(wǎng)應(yīng)用系統(tǒng)與可視化編程技術(shù)。