大螢幕的輸入相容性

在大螢幕裝置上,使用者通常會使用鍵盤、滑鼠、觸控板、觸控筆或遊戲手把與應用程式互動。如要讓應用程式接受外部裝置的輸入,請執行下列步驟:

  • 測試基本鍵盤支援功能,例如 Ctrl+Z 可復原、Ctrl+C 可複製,以及 Ctrl+S 可儲存。如需預設鍵盤快速鍵清單,請參閱「處理鍵盤動作」。
  • 測試進階鍵盤支援功能,例如使用 Tab 鍵和方向鍵瀏覽鍵盤、Enter 鍵確認文字輸入,以及在媒體應用程式中使用空格鍵播放及暫停。
  • 測試基本的滑鼠互動,包括以滑鼠右鍵按一下內容選單、滑鼠遊標懸停圖示變化,以及在自訂元件上以滑鼠滾輪或觸控板捲動事件
  • 測試應用程式專屬的輸入裝置,例如觸控筆、遊戲控制器和音樂應用程式 MIDI 控制器。
  • 考慮進階的輸入支援,讓應用程式能在桌面環境中脫穎而出;舉例來說,觸控板可運用於 DJ 應用程式的交替轉換、遊戲的滑鼠擷取等功能,以及進階使用者專屬的鍵盤快速鍵

鍵盤

應用程式回應鍵盤輸入的方式,有助打造良好的大螢幕使用者體驗。鍵盤輸入有三種,分別為導覽按鍵快速鍵

鍵盤導覽功能極少用在以觸控為主的應用程式,但使用者如果在操作應用程式時使用鍵盤,會希望有鍵盤導覽的功能。對於有無障礙需求的使用者來說,手機、平板電腦、摺疊式裝置和電腦裝置上的鍵盤導覽功能十分重要。

對於許多應用程式,Android 架構會自動處理方向鍵和 Tab 鍵導覽功能。舉例來說,部分可組合項預設為可聚焦,例如 Button 或含有 clickable 修飾符的可組合項;鍵盤導覽一般不需要任何多餘的程式碼即可運作。如要讓預設為無法聚焦的自訂可組合項啟用鍵盤導覽,請新增 focusable 修飾符:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

詳情請參閱「讓可組合項可供聚焦」。

啟用聚焦功能後,Android 架構就會根據位置,建立所有可聚焦元件的對應導覽。通常這些可正常運作,無須做進一步的處理。

不過,如果其中一個可組合函式是無法完全顯示的水平捲動項目,Compose 不一定會為分頁導覽的複雜可組合函式 (例如分頁和清單) 判斷正確的下一個項目。

如要控制焦點行為,請將 focusGroup 修飾符新增至可組合函式集合的父項可組合函式。焦點會移至群組,然後穿過群組,再移至下一個可聚焦的元件,例如:

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

詳情請參閱「透過焦點小組提供一致的導覽功能」。

請只使用鍵盤測試應用程式中每個 UI 元素的存取權。常用的元素應可在無需使用滑鼠或觸控輸入的情況下存取。

提醒您,對於有無障礙需求的使用者來說,鍵盤支援十分重要。

按鍵輸入

使用螢幕虛擬鍵盤處理的文字輸入功能 (IME),例如 a TextField ,應用程式應能如預期地在大螢幕裝置上正常運作,且開發人員不需再另行處理。對於架構無法預測的按鍵動作,應用程式必須自行處理其行為。這對於含有自訂檢視畫面的應用程式而言尤其重要。

例如使用 Enter 鍵傳送訊息的即時通訊應用程式、使用 空格鍵啟動及停止播放的媒體應用程式,以及透過 wasd 鍵控制移動的遊戲。

您可以使用 onKeyEvent 修飾符處理個別按鍵,這個修飾符會在修飾元件收到按鍵事件時呼叫 lambda。您可以使用 KeyEvent#type 屬性,判斷事件是按下按鍵 (KeyDown) 還是放開按鍵 (KeyUp):

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

或者,您也可以覆寫 onKeyUp() 回呼,並為每個收到的鍵盤程式碼新增預期行為:

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
    return when (keyCode) {
        KeyEvent.KEYCODE_ENTER -> {
            sendChatMessage()
            true
        }
        KeyEvent.KEYCODE_SPACE -> {
            playOrPauseMedia()
            true
        }
        else -> super.onKeyUp(keyCode, event)
    }
}

釋放按鍵時,就會發生 onKeyUp 事件。使用回呼可避免按下按鍵或釋放緩慢時,應用程式無需處理多個 onKeyDown 事件。需要偵測按下按鍵時機,或是偵測使用者是否按住按鍵的遊戲和應用程式,可以監聽 onKeyDown 事件,並自行處理重複的 onKeyDown 事件。

詳情請參閱「處理鍵盤動作」。

捷徑

使用硬體鍵盤時,最常使用的是包含 CtrlAltShiftMeta 鍵的鍵盤快速鍵。如果應用程式未實作捷徑,使用者可能會感到困擾。進階使用者也喜歡為常用的應用程式特定工作設定快速鍵。快速鍵能讓應用程式使用起來更方便,使其與沒有快捷鍵的應用程式有所區隔。

常見的快速鍵包括 Ctrl + S (儲存)、Ctrl + Z (復原) 和 Ctrl + Shift + Z (重做)。如需預設快速鍵清單,請參閱「處理鍵盤動作」。

KeyEvent 物件包含下列屬性,用於指出是否按下修飾鍵:

例如:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

如需更多資訊,請參考下列資源:

觸控筆

許多大螢幕裝置都配備觸控筆。Android 應用程式會將觸控筆視為觸控螢幕輸入。部分裝置也可能有 USB 或藍牙繪圖板,例如 Wacom Intuos。Android 應用程式可以接收藍牙輸入,但不支援 USB 輸入。

如要存取觸控筆 MotionEvent 物件,請將 pointerInteropFilter 修飾符新增至繪圖介面。使用處理動作事件的方法實作 ViewModel 類別;將方法做為 pointerInteropFilter 修飾符的 onTouchEvent lambda 傳遞:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

MotionEvent 物件包含事件相關資訊:

歷史點

Android 會批次處理輸入事件,並以每個頁框傳送一次。觸控筆報告事件的頻率會高於螢幕的頻率。建立繪圖應用程式時,請使用 getHistorical API 檢查近期過去的事件:

防止誤觸

使用者使用觸控筆繪圖、書寫或與應用程式互動時,他們的手掌有時可能會碰到螢幕。在系統辨識並忽略手掌誤觸之前,觸控事件 (設為 ACTION_DOWNACTION_POINTER_DOWN) 可以在回報給您的應用程式。

Android 會分派 MotionEvent,藉以取消手掌觸控事件。如果應用程式收到 ACTION_CANCEL,請取消手勢。如果您的應用程式收到 ACTION_POINTER_UP,請檢查是否已設定 FLAG_CANCELED。如已設定,請取消手勢。

不要只檢查 FLAG_CANCELED。在 Android 13 (API 級別 33) 以上版本中,系統會為 ACTION_CANCEL 事件設定 FLAG_CANCELED,但在較舊的 Android 版本中,系統不會設定標記。

Android 12

在 Android 12 (API 級別 32) 以下版本中,系統只能針對單指標觸控事件進行防止誤觸偵測。如果手掌輕觸是唯一的指標,系統則會在動作事件物件上設定 ACTION_CANCEL,以取消該事件。如果其他指標停擺,系統會設定 ACTION_POINTER_UP,但這樣仍不足以進行防止誤觸偵測。

Android 13

在 Android 13 (API 級別 33) 以上版本中,如果手掌輕觸是唯一的指標,系統則會在動作事件物件上設定 ACTION_CANCELFLAG_CANCELED,以取消該事件。如果其他指標停擺,系統則會設定 ACTION_POINTER_UPFLAG_CANCELED

每當應用程式收到具有 ACTION_POINTER_UP 的動作事件時,請檢查 FLAG_CANCELED,以便判斷該事件是否表示防止誤觸 (或其他事件取消)。

筆記應用程式

ChromeOS 有一項特殊意圖,可向使用者顯示已註冊的筆記應用程式。如要將應用程式註冊為記事應用程式,請在應用程式資訊清單中加入以下內容:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

應用程式註冊至系統後,使用者即可將其選定為預設的筆記應用程式。當系統要求新的筆記時,應用程式應建立一個空白筆記,供觸控筆輸入內容。使用者想要為圖片加上註解時 (例如螢幕截圖或下載圖片),應用程式會以 ClipData 啟動,其中包含一或多個具備 content:// URI 的項目。應用程式應建立一項附註,利用第一個附加的圖片做為背景圖片,並進入使用者透過觸控筆在螢幕上繪圖的模式。

測試不使用觸控筆情況下的筆記意圖

[TBD remove section.]

如要測試應用程式是否能正確回應不使用觸控筆情況下的筆記意圖,請使用以下方法在 ChromeOS 中顯示筆記選項:

  1. 切換至開發人員模式,將裝置設為寫入模式
  2. 按下 Ctrl + Alt + F2 鍵開啟終端機
  3. 執行 sudo vi /etc/chrome_dev.conf 指令。
  4. 按下 i 編輯檔案,並在檔案尾端加入一行新的 --ash-enable-palette
  5. 按下 Esc 鍵儲存,輸入 :wq,然後按下 Enter
  6. 按下 Ctrl + Alt + F1 鍵,返回一般 ChromeOS UI
  7. 登出再重新登入

檔案櫃現在應該會顯示觸控筆選單:

  • 輕觸檔案櫃中的觸控筆按鈕,然後選擇「New note」。一個空白的繪圖筆記應該會開啟。
  • 擷取螢幕截圖。從檔案櫃中依序選取「stylus button」>「Capture screen」,或是下載一張圖片。通知中應會出現「為圖片加上註解」的選項。這個選項會啟動應用程式,準備為該圖片加上註解。

支援滑鼠和觸控板

大多數應用程式通常只需要處理三個以大螢幕為主的事件:按一下滑鼠右鍵懸停,以及拖曳

按一下滑鼠右鍵

任何會導致應用程式顯示內容選單的操作 (例如按住清單項目) 也應該回應按一下滑鼠右鍵的事件。

如要處理按一下滑鼠右鍵的事件,應用程式應註冊 View.OnContextClickListener

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

如要進一步瞭解如何建立內容選單,請參閱「建立內容選單」。

懸停

您可以透過處理懸停事件,讓應用程式版面配置看起來更精美流暢。這對自訂元件來說尤其重要:

最常見的兩種情況如下:

  • 變更滑鼠游標圖示,向使用者指出某元素是否具有可互動行為 (例如可點擊或可編輯)
  • 當指標懸停在大型清單或格狀項目上時,對項目加上可見的回饋內容

拖曳

在多視窗環境中,使用者會希望可以在不同應用程式之間拖曳項目。這種情況發生於桌上型裝置,以及處於分割畫面模式的平板電腦、手機和折疊式裝置。

考量使用者是否會將項目拖曳到應用程式中。舉例來說,相片編輯器應預期會接收到相片、音訊播放器應預期會接收到音訊檔案,而繪圖程式應預期會接收到相片。

如要新增拖曳支援功能,請參閱「 拖曳 」和「ChromeOS 上的 Android - 實作拖曳功能」網誌文章。

ChromeOS 的特殊注意事項

進階指標支援

應用程式對滑鼠和觸控板輸入進行進階的處理時,應實作 pointerInput 修飾符,以取得 PointerEvent

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

檢查 PointerEvent 物件,判斷下列事項:

遊戲控制器

部分大螢幕 Android 裝置可支援多達四個遊戲控制器。使用標準 Android 遊戲控制器 API 處理遊戲控制器 (請參閱「支援遊戲控制器」)。

遊戲控制器按鈕會對應至常用的值,同時使用常見的對應。但很遺憾地,並非所有遊戲控制器製造商都遵守相同的對應慣例。如果您可以讓使用者選取不同的熱門控制器對應關係,即可提供更優質的體驗。詳情請參閱「處理遊戲手把按鈕的按下動作」。

輸入平移模式

ChromeOS 預設會啟用輸入平移模式。在大多數的 Android 應用程式中,此模式可讓應用程式在電腦環境中正常運作。例如在觸控板上自動啟用雙指捲動功能、滑鼠滾輪捲動,以及將原始顯示座標對應至視窗座標。一般來說,應用程式開發人員不需自行實作上述任何行為。

如果應用程式執行了自訂的輸入行為 (例如定義自訂的雙指觸控板雙指撥動操作),或是這些輸入平移無法提供應用程式預期的輸入事件,您可以停用輸入平移模式,只要在 Android 資訊清單中加入下列標記即可:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

其他資源