取り外し可能なキーボードの管理

3 つ星評価のアイコン

着脱式キーボードをサポートすると、大画面デバイスでユーザーの生産性を最大限に高めることができます。Android では、キーボードがデバイスに接続またはデバイスから取り外されるたびに構成変更がトリガーされます。これにより、UI 状態が失われる可能性があります。アプリは、状態を保存して復元し、システムにアクティビティの再作成を処理させるか、キーボード構成の変更に関するアクティビティの再作成を制限できます。

いずれの場合も、キーボードに関連するすべてのデータは Configuration オブジェクトに保存されます。構成オブジェクトの keyboard フィールドと keyboardHidden フィールドには、キーボードの種類とその可用性に関する情報が含まれています。

ベスト プラクティス

大画面向けに最適化されたアプリは、ソフトウェア キーボードやハードウェア キーボードから、タッチペン、マウス、トラックパッド、その他の周辺機器まで、あらゆる種類の入力デバイスをサポートします。

外部キーボードのサポートには構成の変更が伴います。構成の変更は、次の 2 つの方法のいずれかで管理できます。

  1. 実行中のアクティビティをシステムに再作成させ、アプリの状態を管理します。
  2. 構成の変更を自分で管理する(アクティビティは再作成されません)。
    • キーボード関連のすべての構成値を宣言する
    • 構成変更ハンドラを作成する

生産性向上アプリでは、テキスト入力などの入力のために UI を細かく制御する必要があることが多く、構成変更を処理する DIY アプローチが役立ちます。

特別なケースとして、ハードウェア キーボードが接続または取り外されたときに、アプリのレイアウトを変更したい場合があります。たとえば、ツールや編集ウィンドウのスペースを増やす場合などです。

Jetpack Compose では、キーボード構成の変更に 2 つの方法で対応できます。

  1. リアクティブ UI: LocalConfiguration.current を使用して、コンポーザブルで現在の構成状態を直接読み取ります。キーボードが接続または取り外されると、UI は自動的に再コンポーズされ、新しい状態に適応します。
  2. 副作用コールバック: ライフサイクル対応の DisposableEffect を使用して、ホスト Activity に構成変更リスナーを登録します。これにより、Activity を再作成したり、ダミーのビュー要素をインスタンス化したりすることなく、UI 以外のイベント(ロギングやアナリティクスなど)をトリガーできます。

材料

  • android:configChanges: アプリ マニフェストの <activity> 要素の属性。アプリが管理する構成変更についてシステムに通知します。
  • LocalConfiguration: 現在のデバイスの Configuration を公開するコンポーザブル ローカル プロバイダ。
  • DisposableEffect: コンポーザブルがコンポジションに入ったときにコードブロックを実行し、コンポジションから離れたときにクリーンアップする Compose の副作用 API。

手順

configChanges 属性を宣言し、キーボード関連の値を追加して、アクティビティの再作成を防止します。次に、LocalConfiguration.current を使用してコンポーズ可能なレイアウトを動的に適応させるか、DisposableEffect を使用して構成リスナーを登録します。

1. configChanges 属性を宣言する

アプリ マニフェストの <activity> 要素を更新して、すでに管理されている構成変更のリストに keyboard|keyboardHidden 値を追加します。

<activity
      …
      android:configChanges="...|keyboard|keyboardHidden">

2. キーボード構成の変更に対応する

Jetpack Compose で構成の変更を処理するには、UI を更新するか、副作用コールバックを実行するかに応じて、次のいずれかのオプションを使用します。

  • オプション A: リアクティブ UI(推奨)

    LocalConfiguration.current からキーボード タイプを直接読み取ります。ハードウェア キーボードが接続または取り外されると、この値が変更され、Compose は UI を自動的に再コンポーズします。

    val configuration = LocalConfiguration.current
    val isPhysicalKeyboardAttached = configuration.keyboard == Configuration.KEYBOARD_QWERTY
    
    if (isPhysicalKeyboardAttached) {
        // Render layout optimized for physical keyboard
    } else {
        // Render default layout
    }

  • オプション B: 副作用リスナー

    構成の変更時に副作用(ロギング、ローカル データベースの更新、アナリティクスのトリガーなど)を実行する必要がある場合は、DisposableEffect を使用して、ホスト ComponentActivity に構成変更リスナーを登録します。

    val context = LocalActivity.current
    DisposableEffect(context) {
        val activity = context as? ComponentActivity
        val listener = Consumer<Configuration> { newConfig ->
            val hasKeyboard = newConfig.keyboard == Configuration.KEYBOARD_QWERTY
            // Trigger non-UI actions, analytics, etc.
        }
        activity?.addOnConfigurationChangedListener(listener)
        onDispose {
            activity?.removeOnConfigurationChangedListener(listener)
        }
    }

結果

これで、実行中のアクティビティを再作成することなく、外部キーボードの接続または接続解除にアプリが応答するようになります。

参考情報

キーボードの着脱などの構成変更時にアプリの UI の状態を保存する方法については、UI の状態を保存するをご覧ください。