입력 방법의 공개 상태 처리

입력 포커스가 수정 가능한 텍스트 필드 안이나 밖으로 이동하면 Android에서는 필요에 따라 입력(예: 터치 키보드)을 표시하거나 숨깁니다. 시스템은 UI와 텍스트 필드가 입력 방법 위에 표시되는 방법도 결정합니다. 예를 들어 화면의 세로 공간이 제한되면 텍스트 필드가 입력 방법 위의 모든 공간을 채울 수 있습니다.

대부분의 앱에 이러한 기본 동작만 있으면 됩니다. 하지만 입력 방법의 가시성과 레이아웃이 레이아웃에 미치는 영향을 더 세밀하게 제어해야 하는 경우도 있습니다. 이 과정에서는 입력 방법의 가시성을 제어하고 응답하는 방법을 설명합니다.

활동이 시작될 때 소프트 키보드를 표시합니다.

Android는 활동이 시작될 때 레이아웃의 첫 번째 텍스트 필드에 포커스를 두지만 소프트 키보드는 표시하지 않습니다. 텍스트 입력이 활동의 기본 작업이 아닐 수 있으므로 이 동작은 적절합니다. 그러나 텍스트 입력이 실제로 주요 작업(예: 로그인 화면)인 경우 소프트 키보드를 기본적으로 표시하는 것이 좋습니다.

활동이 시작될 때 입력 방법을 표시하려면 "stateVisible" 값을 사용하여 android:windowSoftInputMode 속성을 <activity> 요소에 추가합니다. 예:

<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>

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()를 사용하여 뷰 포커스를 요청해야 합니다.

  • 공개 상태는 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());

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);