キーボード アクションを処理する

ユーザーが編集可能なテキスト コンポーネント(TextField など)にフォーカスを合わせると、 デバイスにハードウェア キーボードが接続されている すべての入力がシステムによって処理されます。 キーイベントを処理することで、キーボード ショートカットを提供できます。

デフォルトのキーボード ショートカット

次のキーボード ショートカットは、すぐに使用できます。

キーボード ショートカット アクション ショートカットをサポートするコンポーザブル
Shift+Ctrl+左矢印/右矢印 単語の先頭/末尾までのテキストを選択する BasicTextFieldTextField
Shift+Ctrl+/ 段落の先頭 / 末尾までのテキストを選択する BasicTextFieldTextField
Shift+Alt+上矢印 / 下矢印、または Shift+Meta+左矢印 / 右矢印 テキストの先頭/末尾までのテキストを選択する BasicTextFieldTextField
Shift+/ 文字を選択する BasicTextFieldTextField
Ctrl+A すべて選択 BasicTextFieldTextField
Ctrl+C/Ctrl+X/Ctrl+V コピー/切り取り/貼り付け BasicTextFieldTextField
Ctrl+Z/Ctrl+Shift+Z 元に戻す/やり直す BasicTextFieldTextField
PageDown / PageUp スクロール LazyColumnverticalScroll 修飾子、scrollable 修飾子

重要なイベント

Compose では、個別のキー入力を処理できます。 onKeyEvent 修飾子 この修飾子は、変更されたコンポーネントがキーイベントを受信したときに呼び出されるラムダを受け取ります。キーイベントは KeyEvent オブジェクトとして記述されます。各キーイベントの情報を取得するには、onKeyEvent 修飾子に渡されたラムダ内のオブジェクトを参照します。

キー入力は 2 つのキーイベントを送信します。1 つはユーザーがキーを押すとトリガーされます。 もう一方はキーが離されるとトリガーされます この 2 つのキーイベントは、 KeyEvent オブジェクトの type 属性を参照します。

onKeyEvent ラムダの戻り値は、 キーイベントが処理されるかどうかに関係なく、 アプリがキーイベントを処理する場合は true を返します。これにより、イベントの伝播が停止します。

次のスニペットは、doSomething() 関数を呼び出す方法を示しています。 ユーザーが Box コンポーネントの S キーを放すと、次のようになります。

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

修飾キー

KeyEvent オブジェクトには、次の属性があります。 修飾キーが押されたかどうかに関係なく:

アプリが処理するキーイベントを具体的に記述します。 次のスニペットは doSomething() 関数を呼び出します。 ユーザーが S キーのみを離したときのみ表示されます。 ユーザーが Shift キーなどの修飾子キーを押しても、アプリは関数を呼び出しません。

Box(
  modifier = Modifier.focusable().onKeyEvent{
     if(
       it.type == KeyEventType.KeyUp &&
       it.key == Key.S &&
       !it.isAltPressed &&
       !it.isCtrlPressed &&
       !it.isMetaPressed &&
       !it.isShiftPressed
     ) {
       doSomething()
       true
     } else {
       false
     }
  }
)  {
    Text("Press S key with a modifier key")
}

Space キーと Enter キーのクリック イベント

Space キーと Enter キーもクリック イベントをトリガーします。たとえば、メディアの再生を切り替える(再生または一時停止する)ことができます。 Space キーまたは Enter キー 次のようにクリック イベントを処理します。

MoviePlayer(
   modifier = Modifier.clickable { togglePausePlay() }
)

clickable 修飾子がキーイベントをインターセプトする スペースキーまたはキーが押されると onClick() コールバックを呼び出します。 Enter キーを押す。 そのため、スニペットで Space キーまたは Enter キーを押すと、togglePausePlay() 関数が呼び出されます。

消費されていないキーイベント

未使用のキーイベントがコンポーネントから伝播される 外側のコンポーネントでイベントが発生した場所を示します。 次の例では、InnerComponentS キーがリリースされたときにキーイベントを消費するため、OuterComponentS キーのリリースによってトリガーされたキーイベントを受信しません。そのため、actionB() 関数は呼び出されません。

InnerComponent の他のキーイベント(D キーのリリースなど)は、OuterComponent で処理できます。actionC() 関数が呼び出されるのには、D キーを離すためのキーイベントが OuterComponent に伝播されるためです。

OuterComponent(
    modifier = Modifier.onKeyEvent {
        when {
           it.type == KeyEventType.KeyUp && it.key == Key.S -> {
               actionB() // This function is never called.
               true
           }
           it.type == KeyEventType.KeyUp && it.key == Key.D -> {
               actionC()
               true
           }
           else -> false
        }
    }
) {
    InnerComponent(
        modifier = Modifier.onKeyEvent {
            if(it.type == KeyEventType.KeyUp && it.key == Key.S) {
                actionA()
                true
            } else {
                false
            }
        }
    )
}

onKeyPreviewEvent 修飾子

ユースケースによっては、デフォルトのアクションをトリガーする前にキーイベントをインターセプトする必要があります。TextField にカスタム ショートカットを追加するのは、典型的な方法です。 次のスニペットでは、ユーザーが Tab キーを押して、フォーカス可能な次のコンポーネントに移動できるようにします。

val focusManager = LocalFocusManager.current
var textFieldValue by remember { mutableStateOf(TextFieldValue()) }

TextField(
    textFieldValue,
    onValueChange = {
        textFieldValue = it
    },
    modifier = Modifier.onPreviewKeyEvent {
        if (it.type == KeyEventType.KeyUp && it.key == Key.Tab) {
            focusManager.moveFocus(FocusDirection.Next)
            true
        } else {
            false
        }
    }
)

デフォルトでは、TextField コンポーネントは、キーイベントが onKeyEvent 修飾子で処理されている場合でも、ユーザーが Tab キーを押すたびにタブ文字を追加します。Tab 文字を追加せずにキーボードのフォーカスを移動するには、スニペットのように、キーイベントに関連付けられたアクションをトリガーする前にキーイベントを処理します。onKeyPreviewEvent() ラムダは、true を返すことでキーイベントをインターセプトします。

親コンポーネントは、その子で発生するキーイベントをインターセプトできます。 次のスニペットでは、ユーザーが S キーを押すと、actionForPreview() 関数ではなく previewSKey() 関数が呼び出されます。

Column(
  modifier = Modifier.onPreviewKeyEvent{
    if(it.key == Key.S){
      previewSKey()
      true
    }else{
      false
    }
  }
) {
  Box(
    modifier = Modifier
        .focusable()
        .onPreviewKeyEvent {
            actionForPreview(it)
            false
        }
        .onKeyEvent {
            actionForKeyEvent(it)
            true
        }
  ) {
    Text("Press any key")
  }
}

ユーザーが Tab キーを押しても、Box コンポーネントの onPreviewKeyEvent() ラムダはトリガーされません。onPreviewKeyEvent() ラムダは、まず親コンポーネントで呼び出され、次に子コンポーネントの onPreviewKeyEvent() が呼び出されます。この動作を利用して、画面全体のキーボード ショートカットを実装できます。

参考情報