iT邦幫忙

2022 iThome 鐵人賽

DAY 23
0
Mobile Development

從開發瀏覽器 APP 學習 Android 實戰技巧,並搭上 Jetpack Compose 的列車系列 第 24

[Day23] 從開發瀏覽器 APP 學習 Android 實戰技巧 -- 多指手勢操作 I

  • 分享至 

  • xImage
  •  

EinkBro App 的設計概念是讓畫面上的非必要元素愈少愈好。如果某個使用者對於特定功能的使用頻率比較高的話,可以自行將該功能設定到工具列上,或是從設定畫面中,開啟該功能。

多指手勢操作功能,也是在這樣子的設計概念下誕生的功能。對於一般的使用者來說,常用的功能都已經在工具列上。對於更進階的使用者來說,要一直在工具列上點來點去,可能還是會嫌麻煩。因為 EinkBro 的工具列是可以設定在畫面捲動後自動隱藏。對於這類進階的使用者來說,開啟多指手勢操作就是個能增加生產力的作法。

這篇文章中,我會介紹多指手勢操作的實作方式,以及考慮了什麼樣的機制,確保多指操作不會跟網頁 zoom in/out 的手勢有所衝突。

多點觸控手勢偵測

為了偵測觸控事件,首先要實作 View.OnTouchListener,並覆寫其 onTouch 函式。從它的參數中可以取得一個 MotionEvent instance。MotionEvent 中包含了這次的觸控事件中,包含了幾個觸控點(pointCount)。以 EinkBro 來說,我們現階段想支援的是雙點觸控的手勢操作,所以我們會特別檢查是不是收到了 2 個點的事件:

https://ithelp.ithome.com.tw/upload/images/20221003/20140260qDCUB94WQJ.png

在 onTouch 中,我們必須處理 ACTION_POINTER_DOWN, ACTION_POINTER_UPACTION_MOVE

ACTION_POINTER_DOWN

收到這事件時,要追蹤觸控的啟始點,並且把 inSwipe 設為 true。

when (event.action and MotionEvent.ACTION_MASK) {
    MotionEvent.ACTION_POINTER_DOWN -> {
        scaleFactor = 1.0f
        startPoint0 = event.getPoint(0)
        startPoint1 = event.getPoint(1)
        inSwipe = true
    }

ACTION_MOVE

持續追蹤觸控目前的位置。

MotionEvent.ACTION_MOVE -> {
    if (inSwipe) {
         endPoint0 = event.getPoint(0)
         endPoint1 = event.getPoint(1)
    }
}

ACTION_POINTER_UP

當使用者的手指離開畫面時,我們必須記錄這時的位置,把它跟啟始的位置做比較,再來決定是不是要執行某些功能。

MotionEvent.ACTION_POINTER_UP -> {
    if (inSwipe) { 
        val offSetX = endPoint1.x - startPoint1.x
        val offSetY = endPoint1.y - startPoint1.y
        //Log.i("SWIPE", "offsetX: $offSetX, offsetY: $offSetY")

        if (isValidSwipe(offSetX, offSetY)) { 
            if (abs(offSetX) > abs(offSetY)) {
                if (isSameXDirection()) {
                    if (offSetX > 0) onSwipeRight() else onSwipeLeft()
                }           
            } else {    
                if (isSameYDirection()) {
                    if (offSetY > 0) onSwipeBottom() else onSwipeTop()
                }           
            }           
        }
        inSwipe = false
    }
}

isValidSwipe() 會被用來判斷手指移動的距離是不是有大於預設的 threshold,有超過的話,才會接著再判斷它是屬於朝著 up/down/left/right 的哪個方向移動,然後呼叫對應的功能。

private fun isValidSwipe(offSetX: Int, offSetY: Int) =
        max(abs(offSetX), abs(offSetY)) > SWIPE_THRESHOLD && !isScaling()

private fun isSameXDirection(): Boolean {
    val point0Diff = endPoint0.x - startPoint0.x
    val point1Diff = endPoint1.x - startPoint1.x
    return (point0Diff > 0 && point1Diff > 0) || (point0Diff < 0 && point1Diff < 0)
}

private fun isSameYDirection(): Boolean {
    val point0Diff = endPoint0.y - startPoint0.y
    val point1Diff = endPoint1.y - startPoint1.y
    return (point0Diff > 0 && point1Diff > 0) || (point0Diff < 0 && point1Diff < 0)
}

有了上面這些實作,多指觸控的功能便完成了。這樣是不是就從此過著幸福快樂的日子了呢?當然不是,哪有那麼好的事。

在使用一段時間以後,發現到如果網頁中有一些圖片,原先是有支援縮放的功能,在開啟多指手勢操作後,它就失效了!這時才想到,縮放畫面其實也是會利用到兩根手指,如果沒有特別處理的話,這兩個功能是會打架的。所以,我們還需要實作讓這兩種手勢可以區分開來的邏輯判斷才行。下一篇文章將會說明這部分的處理。

相關連結


上一篇
[Day22] 從開發瀏覽器 APP 學習實戰技巧 -- 遠端編譯程式碼
下一篇
[Day24] 從開發瀏覽器 APP 學習 Android 實戰技巧 -- 多指手勢操作 II 防誤觸縮放
系列文
從開發瀏覽器 APP 學習 Android 實戰技巧,並搭上 Jetpack Compose 的列車31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言