投稿類別 : 資訊類 篇名 : 作者 : 張祐嘉 國立臺南高級海事水產職業學校 電子科三年乙班陳冠穎 國立臺南高級海事水產職業學校 電子科三年乙班曾瀚宇 國立臺南高級海事水產職業學校 電子科三年乙班 指導老師 : 趙景松老師 林瑞進老師
壹 前言 一 研究動機 選擇研究 Android 的手勢係由於當初曾經在 Google 的 Play 商店中找到一款用手勢遊玩的 APP 當下發現 Android 手勢的運用方式之一, 心裡就想著手勢是如何控制的呢? 所以我們決定透過自己撰寫 APP 的知識及研究和網路上的文章來幫助完成一個手勢遊戲的 APP, 文內主要討論手勢運用到的事件 聆聽者 類別 介面及函數及如何應用 Android 的手勢控制來完成一個遊戲 二 研究目的 研究關於手勢是如何運作的, 運用到何種事件, 了解其中的控制, 以及如何運用手勢跟用者互動及使用者如何透過手勢跟程式互動, 藉此研究所學習到的知識來幫助完成一個運用手勢控制方式遊玩的 APP 三 研究方法 透過文獻研究法, 實證研究法及探索性研究法, 我們透過繼承原有的類別架構, 在撰寫自己所需的程式碼, 在網路及書籍上尋找資料及透過每次程式的除錯來學習, 完成這次論文 四 研究流程 我們一開始在九月時, 先擬訂題目, 決定要做什麼專題, 然後開始思考要分配甚麼工作給誰, 然後我們開始翻之前看過關於 Android 的書, 然後開始計畫程式應該如何寫, 之後我們開始實作程式, 並開始使用電腦的手機模擬器測試我們寫的 Android App 接下來我們就一直重複, 發現錯誤, 更改錯誤, 測試程式的正確性, 然後當我們成功將我們的 Android App 寫出計畫的全部功能後, 我們開始彙整我們研究的結果, 得出手勢是如何運用的, 及如何判斷, 最後, 我們將統整所得的結論跟結果寫成小論文 圖一 : 研究流程 ( 圖一資料來源 : 研究者繪製 ) 1
貳 正文 一 GestureOverlayView 類別 ( 一 ) 類別繼承架構 圖二 : 類別架構 ( 圖二資料來源 :Android developer(2017) 2017 年 9 月 21 日, 取自 https://developer.android.com/reference/android/gesture/gestureoverlayview.html) ( 二 )Android 關於手勢操控的方式 表一 : 手勢操控的方式主要分為 2 類, 通過 Android 提供的監聽器來實現 (1) 針對用戶在螢幕上畫出的動作進行移動的偵測 (2) 在螢幕上進行繪製的幾何圖形 ( 多個觸摸事件在螢幕繪製形狀 ) ( 表一資料來源 : 研究者自行繪製 ) ( 三 ) 繪製手勢 表二 :GestureOverlayView 繪製手勢需要一個 View 介面, 而這個介面由 GestureOverlayView 來完成 (1) GestureOverlayView 主要負責顯示和處理手指在螢幕上所繪製的手勢 (2)GestureOverlayView 繼承自 View 類別, 所以他擁有 ontouchevent 方法 ( 表二資料來源 : 研究者自行繪製 ) 如果父類別的方法不符合子類別的需求, 我們可以在子類別宣告同名 同參數和傳回值的方法來取代父類別的方法, 稱為覆寫 (Override) ( 陳會安,2015 年 ) 而 GestureOverlayView 覆寫了 View 的 ontouchevent 方法, 來判斷是什麼手勢, 以及接收到手勢之後應該如何處理 2
二 手勢事件判斷與處理 圖三 : 手勢事件判斷方法 ( 圖三資料來源 : 研究者繪製 ) ( 一 ) 事件處理前判斷 1 判斷元件是否啟用 (isenabled()) 2 判斷是否有繪製手勢, 如果無則使用 CANCEL 事件來淡化之前手勢並消除 3 使用 processevent(motionevent event) 方法判斷為何種事件 ( 二 )processevent 判斷事件後的處理方法 當 MotionEvent.getAction() 取得的動作為 MotionEvent.ACTION_DOWN 使用 ontouch 方法, 如下 ( 圖四 ) 所示 圖四 :touchdown 事件處理方法 ( 圖四資料來源 : 研究者繪製 ) 1 獲取手指點下螢幕的座標值 x,y 同時把他們的數值給全域變數 mx,my, 其中 mtotallength 變數代表手勢的總長度, 再調用 touchdown 時, 還沒繪製手勢, 這時 mtotallength 為 0,mIsGesturing 變數判斷是否在繪製手勢,false 表示不在繪製手勢 2 根據條件判斷, 設置畫筆的顏色, 處理手勢畫筆的狀態, 及創建 Gesture 對象等 3
3 將 (1) 拿到的 x,y 座標值和 event.geteventtime() 的值作為 GesturePoint 函數的參數創 建 GesturePoint 對象, 並將 GesturePoint 對象加進 mstrokebuffer 集合 4 將 (1) 得到的 x,y 座標值作為 mpath 畫筆路徑的起始點 5 尋找 OnGestureListener 的集合聆聽者, 調用實現 OnGestureListener 接口的 ongesturestarted() 方法 ( 表示手勢開始繪製 ) 6 當處理完後即使用 invalidate() 方法重新繪製 View( 用來顯示手勢 ) MotionEvent.getAction() 傳回的觸控類型中, 有一 MotionEvent.ACTION_MOVE, 會在手指按住時移動發生 ( 施威銘,2016 年 ) 如下 ( 圖五 ) 所示 圖五 :touchmove 方法事件處理 ( 圖五資料來源 : 研究者繪製 ) 7 touchmove 方法返回值類型為 Rect( 一個矩形區域 ), 若返回值不為空值, 則呼叫 invalidate(rectrect) 刷新 GestureOverlayView 8 得到當前手指所並螢幕位置的 x,y 座標值, 將 x,y 值與調用 touchdown() 方法得到的 x,y 值相減後取絕對值, 得到偏移量 dx,dy 9 dx 或者 dy 大於 GestureStroke.TOUCH_TOLERANCE 時 ( 默認為 3), 執行繪製手勢 10 畫筆路徑使用 quadto() 方法執行貝賽爾曲線計算, 以得到平滑曲線 11 矩形區域根據手勢繪制控制點和結束點的位置不斷更新, 繪出手勢軌跡 ( 每次 調用 touchmove() 時, 逐點更新從而達成一定軌跡的幾何圖形, 即手勢的雛形 12 將 (2) 得到的 x,y 座標值和 event.geteventtime() 的值作为 GesturePoint 函數的的參數 4
創建 GesturePoint 對象, 並將對象加進 mstrokebuffer 集合 ( 保存手勢的相關信息 ) 13 當調用 GestureOverlayView 的 addongestureperformedlistener 方法添加聆聽者 ongestureperformedlistener 時,mHandleGestureActions 設值為 true 計算最小邊界, 然後根據最小邊界進行條件判斷, 進而設置 misgesturering 为 true, 以及設置手勢繪製顯示的顏色 14 touchmove() 最後, 尋找 OnGestureListener 接口的集合聆聽者, 使用實現 OnGestureListener 接口的 ongesture 方法 ( 手勢正在繪製 ) 如果事件為 ACTION_UP 或 ACTION_CANCEL 事件時, 則使用 touchup 方法, 該方法主要動作如下 ( 圖六 ) 所示 : 圖六 :touchup 方法事件處理 ( 圖六資料來源 : 研究者繪製 ) 15 將 mislisteningforgesture 值變更為 false( 取消聆聽狀態 ) 16 判斷現在是否存在 mcurrentgesture(gesture 類型 ), 此變數為在執行 touchdown 方法時創建並給予值的 Gesture 對象, 也可以用 setgesture 方法給予值 ; (mcurrentgesture 就是用戶所繪製的整個手勢 ) 17 若 mcurrentgesture 不是空值, 則之前调用 touchdonw 和 touchmove 得到的 GesturePoint 组成的集合 mstrokebuffer 作為 GestureStroke 函數的參數, 建立 GestureStroke 對象 之後將 GestureStroke 對象調用 addstroke 方法添加到 mcurrentgesture 中 18 若 touchup 方法的第二參數給予的布林值為 false( 執行 ACTION_UP 事件時 ), 調用 ongestureended() 方法 接著調用 clear() 方法, 將繪製的手勢清除 ( 淡出螢幕 ) 5
19 若 touchup 方法的第二參數給予的布林值為 true( 執行 ACTION_CANCEL 事件時 ), 調用 clear() 方法回收 mcurrentgesture 對象 清除畫筆, 淡出螢幕等處理 ( 三 ) 取得手勢庫和應用 手勢在操控之前要先取得之前創立的手勢庫, 並把手勢庫實例的載入進程式, 之後程式才可以依手勢庫進行判斷, 主要由 GestureLibrarys,GestureLibrary 類別來完成, 讀取手勢庫的方式有從檔案名稱取得, 檔案路徑取得以及應用程式本身的文件夾中取得或是從資源庫中取得手勢, 如 ( 圖七 ) 所示 圖七 : 取得手勢庫的方法 ( 圖七資料來源 : 研究者繪製 ) 1 GestureLibrarys, 名字和 (2) GestureLibrary 非常像, 這個檔案裡的方法全是 static 方法, 一般通過 GestureLibrarys 取得 GestureLibrary, 透過打開文件方式,Gesture 可以個別保存到文件裡, 讀取完手勢後還必須加載才能使用 2 GestureLibrary, 這是一個保存手勢的集合,GestureLibrary 可以包含很多的手 勢, 然後通過 GestureLibrary 來操作手勢如, 增加手勢, 刪除手勢, 判斷手 勢, 保存手勢, 加載手勢等, 可進行操作如 ( 圖八 ) 所示 圖八 : 手勢庫的操作 ( 圖八資料來源 : 研究者繪製 ) 6
( 四 ) 判斷手勢的方法 圖九 : 判斷手勢的 2 種方法 ( 圖九資料來源 : 研究者繪製 ) ArrayList<Prediction>recognize(Gesture gesture): 從手勢庫識別與 gesture 匹配的手勢, 運用了 2 種判定的手段, 一個是用存檔在手勢庫的手勢名稱, 以及手勢相似度的判斷 使用者的手勢會與手勢庫中的手勢比對 ( 黃彬華,2015 年 ) 以分數跟名稱判定 参 結論 一 研究發現 從以上研究我們知道了,Android 的手勢控制主要是由四種動作事件來觸發, 分別為按下螢幕 在螢幕上移動 離開螢幕 取消動作的事件, 而 GestureOverlayView 則是接受這些事件的畫布 ( c a n v a s ), 在執行繪畫手勢時會一直使用 i n v a l i d a t e ( ) 方法更新 GestureOverlayView 上所顯示的手勢圖像, 我們可以使用 GestureLibrary.addGesture() 方法來添加手勢, 並用 GestureLibrary.save() 方法來保存手勢庫以供下次使用, 當下次要使用該手勢庫時用 GestureLibrarys 取得手勢庫再用 GestureLibrary.load() 方法載入手勢庫, 即可使用 而手勢庫中手勢的判斷非常簡單只要取得 Prediction.recognize 的手勢集合, 再使用其 中 Prediction 的手勢名稱判斷及 Prediction 的手勢分數判斷即可以確定是哪個手勢, 並使 用之 二 研究成果 經由這次研究, 我們成功掌握了如何運用 Android 的手勢控制, 並成功寫出了一套依 照手勢遊玩的 Android App, 如 ( 圖十 十一 十二 十三所示 )( 圖為電腦模擬器畫面, 故 有指標 ) 7
圖十 : 遊戲介面圖十一 : 繪製手勢圖十二 : 攻擊動畫圖十三 : 被攻擊 ( 圖十 十一 十二 十三資料來源 : 研究者自行拍攝 ) 一開始經過遊戲介紹後選擇繼續, 將會進入選擇介面, 實做了選擇關卡的戰鬥, 升級能力的升級, 介紹的說明 & 製作者, 開起討論區的開啟 FB 專頁, 分享討論區的分享 FB 頁, 以及練習用的教學關卡, 這裡是玩家的主介面, 負責玩家剛進入遊戲時的介面, 玩家可以在這裡決定要進行的動作, 玩家想要升級或戰鬥或是儲存資料到雲端都在這裡選擇, 如 ( 圖十 ) 所示 而在戰鬥方面, 玩家的攻擊是運用繪製手勢, 繪製完成後會經由 ongestureperformed 聆聽者來獲取事件, 在使用載入後的手勢庫來確認, 使用手勢庫的 recognize(gesture) 來比對 gesture 取得可能吻合之手勢資料, 並存入 list 中, 然後取得集合中分數最高的項目來判斷所繪手勢, 如果所繪手勢與下方圖版相同, 則發動攻擊與攻擊動畫, 並對怪物目前的血量進行運算, 然後再檢查怪物血量是否為零, 當怪物血量為零時將會進入勝利畫面, 而攻擊動畫則使用原本為隱藏的 ImageView, 當玩家手勢繪製完成將會將 ImageView 顯示, 然後啟動一個執行緒來負責旋轉 ImageView, 然後顯示一秒後, 將會把 ImageView 設成隱藏的並停止動畫, 而如果這時怪物如果已經死了, 就不撥放動畫, 因為如果怪物已經死了, 跳到勝利畫面, 卻還顯示攻擊動畫, 將會顯得非常奇怪, 如 ( 圖十一, 十二 ) 所示 而怪物攻擊的部分則使用執行緒, 我們總共使用十個執行緒, 每個執行緒皆使用一秒的延遲, 之後會啟動下一個執行緒, 檢查角色血量是否為零, 還有是否在暫停狀態, 如果為零時則跳到死亡畫面, 到第十個時將會進行判斷, 如果角色血量不為零, 怪物將會進行攻擊並撥放攻擊動畫, 而怪物的攻擊動畫跟角色的攻擊動畫使用一樣的方式, 運用執行緒來使 ImageView 顯示, 然後一秒後隱藏, 但是怪物的 ImageView 並沒有旋轉, 而跟角色攻擊一樣如果角色死亡將不會撥放動畫, 如 ( 圖十三 ) 所示 8
而在升級介面如圖 ( 十四 ) 所示, 列出了角色目前的攻擊力, 跟血量值, 然後當錢足夠時, 按下升級, 將會立即刷新顯示的值, 例如 : 按下升級攻擊時, 角色目前攻擊將會刷新為 40 而升級所需金錢也會刷新成 125 當然, 只會刷新其對應的值, 而不會更改到其他不屬於升級攻擊有關的值, 而升級生命時亦同於升級攻擊更新的方式, 並且為了安全起見, 每次升級完後都會進行存檔再讀檔確認更新的動作 圖十四 : 升級介面 ( 圖十四資料來源 : 研究者自行拍攝 ) 圖十五 : 雲端介面圖十六 : 上傳雲端圖十七 : 讀取雲端圖十八 : 雲端資料 ( 圖十五 十六 十七 十八資料來源 : 研究者自行拍攝 ) 而我們也實作了儲存到雲端的功能如 ( 圖十五 ) 所示, 如果欲更換手機時, 只要把遊戲的紀錄, 儲存到雲端, 就能更換, 而在更換時, 為了確保沒有多台手機同時使用紀錄, 當上傳到雲端, 手機端資料會重置如 ( 圖十六 ) 所示, 然後讀取時也一樣, 讀取完會確認是否正確數值, 然後確定讀取後, 將會刪除在雲端庫的紀錄如 ( 圖十七 ) 所示, 而雲端存檔及讀檔則採用 Json 格式為存檔方式, 在雲端資料庫觀看時, 前面是鍵值名稱, 後面是實際儲存的資料, 如 ( 圖十八 ) 所示, 而當讀取資料時, 則是由第一個鍵值開始讀值, 所以我們必須寫一個類別來負責接收雲端的資料, 然後在該類別裡面寫方法, 負責把各個不同類別所需的資料分配給該類別運用 9
肆 引註資料 陳會安 (2015 年 ) 新觀念 Android 程式設計範例教本 台北市 : 旗標出版股份有限公司 施威銘 (2016 年 ) Android 程式設計教本之無痛起步 台北市 : 旗標出版股份有限公司 黃彬華 (2015 年 ) Android 6~5.x App 開發教戰手冊 - 使用 Android Studio 台北市 : 碁峰資訊股份 有限公司 Android developer (2017 年 ) 2017 年 9 月 21 日, 取自 https://developer.android.com/reference/android/gesture/gestureoverlayview.html 10