處理輸入法顯示設定

當輸入焦點移入或移出可編輯文字欄位時,Android 會視情況顯示或隱藏輸入文字 (例如螢幕小鍵盤)。系統也會決定 UI 和文字欄位在輸入法上方的顯示方式。舉例來說,當螢幕上的垂直空間受到限制時,文字欄位可能會填滿輸入法上方的所有空間。

就大多數應用程式而言,這些預設行為都至關重要。然而,在某些情況下,您可能會希望進一步控管輸入法的瀏覽權限,以及它對版面配置的影響。本課程將說明如何控管及回應輸入法的顯示設定。

活動開始時顯示螢幕鍵盤

雖然 Android 會在活動啟動時聚焦於版面配置的第一個文字欄位,但不會顯示螢幕鍵盤。這個行為是適當的,因為輸入文字可能不是活動中的主要工作。然而,如果輸入文字確實是主要工作 (例如在登入畫面中),則您可能會想要預設顯示螢幕鍵盤。

如要在活動開始時顯示輸入法,請將 android:windowSoftInputMode 屬性加入含有 "stateVisible" 值的 <activity> 元素。例如:

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

指定 UI 的回應方式

當螢幕上顯示螢幕鍵盤時,即可減少應用程式 UI 的可用空間。系統會決定如何調整 UI 的可見部分,但可能無法正確調整。為確保應用程式能獲得最佳行為,請指定您希望系統在剩餘空間中顯示 UI 的方式。

如要在活動中宣告您偏好的處理方式,請在資訊清單的 <activity> 元素中搭配其中一個「調整」值使用 android:windowSoftInputMode 屬性。

舉例來說,如要確保系統可將版面配置調整為可用空間,以便存取所有版面配置內容 (即使需要捲動畫面),請使用 "adjustResize"

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

您可以結合上述調整規格與上一節的初始螢幕鍵盤瀏覽權限規格:

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

如果 UI 包含使用者在執行文字輸入後或執行後立即存取的控制項,指定 "adjustResize" 非常重要。舉例來說,如果您使用相對版面配置在畫面底部放置按鈕列,使用 "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() 要求檢視焦點)。

  • 顯示設定也可能受到 showSoftInput() 使用的 android:windowSoftInputMode 屬性和標記的影響。

在某些用途中,例如活動開始時,不符合部分必要條件。系統不會將檢視畫面視為連接到螢幕鍵盤、忽略 showSoftInput() 呼叫,且不會向使用者顯示螢幕鍵盤。

為確保螢幕鍵盤穩定顯示,您可以使用下列替代方案:

Kotlin

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

Java

editText.requestFocus();
WindowCompat.getInsetsController(getWindow(), editText).show(WindowInsetsCompat.Type.ime());

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_HIDDENSOFT_INPUT_STATE_ALWAYS_HIDDEN 標記,以防螢幕鍵盤意外隱藏。

系統通常會自動隱藏螢幕鍵盤

在大部分情況下,系統會處理隱藏螢幕鍵盤。這可以是下列任一情況:

  • 使用者在文字欄位中完成工作。
  • 使用者按下返回鍵或滑動返回瀏覽手勢。
  • 使用者前往其他應用程式,且其他應用程式已在檢視畫面取得焦點時設定 SOFT_INPUT_STATE_HIDDENSOFT_INPUT_STATE_ALWAYS_HIDDEN 標記。

根據先前的系統行為手動隱藏螢幕鍵盤

應用程式在某些情況下 (例如文字欄位在 View.OnFocusChangeListener.onFocusChange 中失去焦點) 時,必須手動隱藏螢幕鍵盤。請審慎使用這項技術,關閉螢幕鍵盤以無預警地對使用者體驗造成負面影響。

如果應用程式手動隱藏螢幕鍵盤,您必須知道螢幕鍵盤是以明確隱含的方式顯示:

  • 系統會將螢幕鍵盤視為在呼叫 showSoftInput()明確顯示。

  • 相反地,在下列任一情況下,螢幕鍵盤會被視為以隱含方式顯示:

    • 套用 android:windowSoftInputMode 時,系統會顯示螢幕鍵盤。
    • 您的應用程式已將 SHOW_IMPLICIT 傳遞至 showSoftInput()

一般來說,無論要求方式為何,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);