入力方法の表示を処理する

入力フォーカスが編集可能なテキスト フィールド内外に移動すると、Android では、入力(画面キーボードなど)に応じて入力の表示 / 非表示が切り替わります。また、入力方法の上に UI とテキスト フィールドをどのように表示するかもシステムによって決定されます。たとえば、画面の垂直方向のスペースに制約がある場合、テキスト フィールドが入力方法の上にあるすべてのスペースに収まることがあります。

ほとんどのアプリでは、このデフォルトの動作で十分です。ただし、入力方法の表示や入力方法がレイアウトに与える影響を細かく制御したい場合もあります。このレッスンでは、インプット メソッドの表示の制御と対応について説明します。

アクティビティの開始時にソフト キーボードを表示する

Android では、アクティビティの開始時にレイアウトの最初のテキスト フィールドがフォーカスされますが、ソフト キーボードは表示されません。テキストの入力がアクティビティの主要タスクではない場合があるため、この動作は適切です。ただし、ログイン画面などでテキストの入力が実際に主要なタスクである場合は、デフォルトでソフト キーボードを表示するようにすることをおすすめします。

アクティビティの開始時に入力方法を表示するには、android:windowSoftInputMode 属性を <activity> 要素に追加し、値 "stateVisible" を指定します。次に例を示します。

<application ... >
    <activity
        android:windowSoftInputMode="stateVisible" ... >
        ...
    </activity>
   ...
</application>

UI の対応方法を指定する

ソフト キーボードが画面に表示されると、アプリの UI で使用可能なスペースの量が減少します。UI の表示部分の調整方法はシステムが決定しますが、正しく調整されない可能性があります。アプリの動作を最適にするには、残りのスペースで UI をどのように表示するかを指定します。

アクティビティで優先する処理を宣言するには、マニフェストの <activity> 要素で android:windowSoftInputMode 属性を使用して「adjust」値のいずれかを指定します。

たとえば、使用可能なスペースに合わせてレイアウトのサイズが自動的に変更されるようにし、スクロールが必要な場合でもすべてのレイアウト コンテンツにアクセスできるようにするには、"adjustResize" を使用します。

<application ... >
   <activity
       android:windowSoftInputMode="adjustResize" ... >
       ...
   </activity>
   ...
</application>

調整仕様は、前のセクションの初期ソフト キーボード表示仕様と組み合わせることができます。

<activity
    android:windowSoftInputMode="stateVisible|adjustResize" ... >
    ...
</activity>

"adjustResize" の指定は、テキスト入力の直後または実行中にユーザーがアクセスする必要があるコントロールが UI に含まれている場合に重要です。たとえば、相対レイアウトを使用して画面下部にボタンバーを配置する場合、"adjustResize" を使用すると、レイアウトのサイズを変更して、ボタンバーがソフト キーボードの上に表示されます。

オンデマンドでソフト キーボードを表示する

アクティビティのライフサイクルに、入力方法を表示する必要があるメソッドがある場合は、InputMethodManager を使用して表示できます。

たとえば、次のメソッドでは、ユーザーが何かを入力することが求められる View を受け取り、requestFocus() を呼び出してフォーカスを与えてから、showSoftInput() を呼び出して入力メソッドを開きます。

Kotlin

fun showSoftKeyboard(view: View) {
   if (view.requestFocus()) {
       val imm = getSystemService(InputMethodManager::class.java)
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
   }
}

Java

public void showSoftKeyboard(View view) {
   if (view.requestFocus()) {
       InputMethodManager imm = getSystemService(InputMethodManager.class);
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
   }
}

ソフト キーボードを確実に表示する

状況によっては、アクティビティの開始時など、InputMethodManager.showSoftInput() を使用してソフト キーボードを表示すると、ソフトウェア キーボードがユーザーに表示されない場合があります。

showSoftInput() 使用時のソフト キーボードの可視性は、次の条件に依存します。

  • ビューはすでにソフトウェア キーボードに接続されている必要があります。(そのために、ウィンドウをフォーカスし、エディタビューが View.requestFocus() でビューのフォーカスをリクエストする必要があります)。

  • 可視性は、android:windowSoftInputMode 属性と、showSoftInput() で使用されるフラグによっても影響を受ける場合があります。

特定のユースケース(アクティビティの開始時など)では、これらの必須条件の一部が満たされません。システムはビューがソフトウェア キーボードに接続されているとはみなさず、showSoftInput() 呼び出しを無視し、ソフト キーボードはユーザーに表示されません。

ソフトウェア キーボードが確実に表示されるようにするには、次の方法を使用します。

  • (推奨)WindowInsetsControllerCompat を使用します。次のコード スニペットに示すように、このオブジェクトは Activity.onCreate() のときにソフト キーボードを表示します。ウィンドウがフォーカスされた後に、呼び出しがスケジュールされることが保証されます。

Kotlin

editText.requestFocus()
WindowCompat.getInsetsController(window, editText)!!.show(WindowInsetsCompat.Type.ime())

Java

editText.requestFocus();
WindowCompat.getInsetsController(getWindow(), editText).show(WindowInsetsCompat.Type.ime());
  • 実行可能物を投稿する。これにより、アプリは View.onWindowFocusChanged() からウィンドウ フォーカス イベントを受信するまで待機してから showSoftInput() を呼び出します。

Kotlin

class MyEditText : EditText() {
  ...
  override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
    if (hasWindowFocus) {
      requestFocus()
      post {
        val imm: InputMethodManager = getSystemService(InputMethodManager::class.java)
        imm.showSoftInput(this, 0)
      }
    }
  }
}

Java

public class MyEditText extends EditText {
  ...
  @Override
  public void onWindowFocusChanged(boolean hasWindowFocus) {
    if (hasWindowFocus) {
      requestFocus();
      post(() -> {
        InputMethodManager imm = getSystemService(InputMethodManager.class);
        imm.showSoftInput(this, 0);
      });
    }
  }
}

ランタイム可視性フラグを慎重に扱う

実行時にソフト キーボードの表示を切り替える場合は、これらのメソッドに特定のフラグ値を渡さないように注意してください。たとえば、アプリがアクティビティの起動時に Activity.onCreate()View.getWindowInsetsController().show(ime()) を呼び出すときにソフト キーボードが表示されることを想定している場合、アプリ デベロッパーは、初回起動時にソフト キーボードが予期せず非表示になった場合に備えて、SOFT_INPUT_STATE_HIDDEN フラグや SOFT_INPUT_STATE_ALWAYS_HIDDEN フラグを設定しないように注意する必要があります。

通常、ソフト キーボードは自動的に非表示になります

ほとんどの場合、ソフト キーボードを非表示にする処理はシステム側で行われます。次のいずれかのケースが考えられます。

  • ユーザーがテキスト フィールドのタスクを完了します。
  • ユーザーが [戻る] ナビゲーションで [戻る] キーを押すか、スワイプのジェスチャーを行った。
  • ユーザーが別のアプリに移動し、その別のアプリがビューがフォーカスを取得したときに SOFT_INPUT_STATE_HIDDEN フラグまたは SOFT_INPUT_STATE_ALWAYS_HIDDEN フラグを設定している。

以前のシステム動作に基づいてソフト キーボードを手動で非表示にする

状況によっては、アプリでソフト キーボードを手動で非表示にする必要があります(View.OnFocusChangeListener.onFocusChange でテキスト フィールドがフォーカスを喪失した場合など)。ソフト キーボードを閉じると、ユーザー エクスペリエンスが予期せず損なわれるため、この方法は慎重に使用してください。

アプリでソフト キーボードを手動で非表示にする場合は、ソフト キーボードが明示的または暗黙的のどちらに表示されたかを確認する必要があります。

  • ソフト キーボードは、showSoftInput() を呼び出した後に明示的に表示されたとみなされます。

  • 逆に、ソフト キーボードは、次のいずれかの条件で暗黙的に示されたと見なされます。

    • android:windowSoftInputMode の適用中にソフト キーボードが表示されました。
    • アプリが SHOW_IMPLICITshowSoftInput() に渡しました。

通常、hideSoftInputFromWindow() はリクエスト方法にかかわらずソフト キーボードを非表示にしますが、HIDE_IMPLICIT_ONLY を使用すると、暗黙的にリクエストされたソフト キーボードを閉じるだけに制限できます。

ソフト キーボードの上にダイアログまたはオーバーレイ ビューを表示する

エディタ アクティビティでは、ソフト キーボードの上に編集不可能なダイアログまたはオーバーレイ ウィンドウを作成しなければならない場合があります。

アプリにはいくつかのオプションがあります。これについては、次のセクションで説明します。

要約すると、ウィンドウをターゲットとするソフト キーボードのウィンドウ フラグを正しく処理して、垂直(Z レイヤ)の順序に関する次の要件を満たすようにします。

  • フラグなし(通常のケース): ソフト キーボード レイヤの背後で、テキストを受け取ることができます。
  • FLAG_NOT_FOCUSABLE: ソフト キーボード レイヤの上にありますが、テキストを受信できません。
  • FLAG_ALT_FOCUSABLE_IM: ソフト キーボード レイヤの上にあります。フォーカスできますが、ソフト キーボードには接続されていません。また、その下にあるすべてのビューがソフト キーボードに接続できないようにブロックします。これは、ソフト キーボード レイヤの上にテキスト入力を使用しないアプリ ダイアログを表示する場合に役立ちます。
  • FLAG_NOT_FOCUSABLEFLAG_ALT_FOCUSABLE_IM: ソフト キーボード レイヤの背後にありますが、テキストを受信できません。
  • FLAG_NOT_FOCUSABLEFLAG_NOT_TOUCH_MODAL: ソフト キーボードの上部で、タッチイベントがウィンドウを通過してソフト キーボードに到達できるようにします。

ダイアログを作成する

ダイアログをソフト キーボードの上に維持し、ソフト キーボードがフォーカスを取得しないようにするには、FLAG_ALT_FOCUSABLE_IM ダイアログ ウィンドウ フラグを使用します。

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog on top of soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog on top of soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);
mDialog = builder.create();
mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

オーバーレイ ビューを作成する

ソフト キーボードのターゲット アクティビティによって、TYPE_APPLICATION_OVERLAY ウィンドウ タイプと FLAG_ALT_FOCUSABLE_IM ウィンドウ フラグを指定して、オーバーレイ ビューを作成します。

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION, /* Overlay window type */
  WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  /* Allow touch event send to soft keyboard behind the overlay */
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
        | FLAG_NOT_TOUCH_MODAL, /* Allow touch event send to soft keyboard behind the overlay */
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);

ソフト キーボードの下にダイアログまたはビューを表示する

場合によっては、アプリで次のプロパティを持つダイアログやウィンドウを作成する必要があります。

  • エディタ アクティビティによってリクエストされたソフト キーボードの下に表示され、テキスト入力の影響を受けないようにします。
  • ソフト キーボードのインセット サイズの変更を認識して、ダイアログやウィンドウのレイアウトを調整します。

この場合、アプリにはいくつかの選択肢があります。以降のセクションでは、これらのオプションについて説明します。

ダイアログを作成する

FLAG_NOT_FOCUSABLE ウィンドウ フラグと FLAG_ALT_FOCUSABLE_IM ウィンドウ フラグの両方を設定して、ダイアログを作成します。

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog behind soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog behind soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);

mDialog = builder.create();
mDialog.getWindow()
    .addFlags(FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

オーバーレイ ビューを作成する

FLAG_NOT_FOCUSABLE ウィンドウ フラグと FLAG_ALT_FOCUSABLE_IM ウィンドウ フラグの両方を設定して、オーバーレイ ビューを作成します。

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION,  /* Overlay window type */
  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
      or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM,
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);