본문 바로가기
Development/Android

[Android] 키보드가 화면 레이아웃에 영향 주지 않게 하는 방법

by 은스타 2020. 4. 8.
반응형
Android 키보드 레이아웃 영향 차단 완벽 가이드

Android 키보드 레이아웃 영향 차단 완벽 가이드 - windowSoftInputMode부터 실전 구현까지

개요

Android 앱 개발 과정에서 소프트 키보드가 화면에 표시될 때 레이아웃이 의도치 않게 밀려 올라가거나 중요한 UI 요소가 가려지는 문제는 개발자라면 한 번쯤 겪게 되는 흔한 이슈입니다. 특히 로그인 화면, 회원가입 폼, 채팅 앱 등 사용자 입력이 빈번한 화면에서 이러한 문제는 사용자 경험을 크게 저해할 수 있습니다.

키보드가 나타나면 화면 하단의 중요한 버튼이 가려지거나, 전체 레이아웃이 위로 밀려 올라가 UI가 망가지거나, 입력 필드가 키보드에 가려져 사용자가 자신이 무엇을 입력하는지 확인할 수 없는 상황이 발생합니다.

이 가이드에서는 Android 매니페스트 설정부터 ConstraintLayout, ScrollView 활용법, 프로그래밍 방식의 키보드 제어, 실전 채팅 앱 UI 구현까지 키보드 문제를 해결하는 모든 방법을 상세히 다룹니다. 각 해결 방법의 원리와 실제 코드 예제를 통해 여러분의 앱에서 키보드 관련 문제를 완벽하게 해결할 수 있도록 안내합니다.
목차
1. Android 앱에서 키보드 문제가 발생하는 이유
2. AndroidManifest.xml 설정으로 키보드 제어하기
3. ConstraintLayout으로 키보드 문제 해결하기
4. ScrollView 및 프로그래밍 방식의 키보드 제어
5. 실전 채팅 앱 UI 구현 및 디바이스별 테스트

#1. Android 앱에서 키보드 문제가 발생하는 이유
Android 앱을 개발하다 보면 가장 흔하게 마주치는 문제 중 하나가 바로 소프트 키보드가 화면 레이아웃을 밀어올리거나 가리는 현상입니다. 이러한 문제가 발생하는 근본적인 이유와 해결 방향을 먼저 이해해야 합니다.
1) 키보드 표시 시 발생하는 주요 문제점
키보드가 나타나면 다음과 같은 문제가 발생할 수 있습니다.
① 화면 하단의 중요한 버튼이나 콘텐츠가 키보드에 완전히 가려지는 현상
② 전체 레이아웃이 위로 밀려 올라가면서 상단 콘텐츠가 잘려 UI가 망가지는 현상
③ 입력 필드가 키보드에 가려져 사용자가 자신이 무엇을 입력하는지 볼 수 없는 현상
④ 키보드가 나타났다 사라질 때마다 레이아웃이 재배치되면서 화면이 깜빡이는 현상
. . . . .
2) 키보드 문제의 근본 원인
Android 시스템은 키보드가 표시될 때 액티비티의 windowSoftInputMode 설정에 따라 화면을 처리합니다. 이 설정이 명시되지 않으면 시스템이 기본값으로 동작하는데, 이 기본 동작이 개발자가 의도한 UI 레이아웃과 맞지 않을 때 문제가 발생합니다.
특히 ConstraintLayout이나 LinearLayout을 잘못 사용하거나, 화면 높이를 match_parent로 고정한 상태에서 스크롤 처리를 하지 않으면 키보드가 콘텐츠를 가리거나 레이아웃이 의도치 않게 변형됩니다.
. . . . .
3) 해결 방법의 기본 방향
키보드 문제를 해결하기 위한 접근 방법은 크게 세 가지로 나눌 수 있습니다.
(1) 매니페스트 설정 방식
① AndroidManifest.xml에서 windowSoftInputMode 속성을 적절히 설정하여 시스템 레벨에서 키보드 동작을 제어하는 방법
② 가장 간단하고 빠르게 적용할 수 있는 방법이지만 세밀한 제어에는 한계가 있음
(2) 레이아웃 최적화 방식
① ConstraintLayout을 활용하여 키보드가 나타나도 유연하게 대응할 수 있는 UI 구조를 설계하는 방법
② ScrollView를 사용하여 키보드가 나타나면 자동으로 스크롤이 가능하도록 처리하는 방법
(3) 프로그래밍 방식
① InputMethodManager를 사용하여 코드에서 직접 키보드를 표시하거나 숨기는 방법
② 키보드 높이를 측정하여 그에 따라 UI를 동적으로 조정하는 방법

#2. AndroidManifest.xml 설정으로 키보드 제어하기
가장 먼저 시도해볼 수 있는 방법은 AndroidManifest.xml 파일에서 액티비티의 windowSoftInputMode 속성을 설정하는 것입니다. 이 방법은 코드 수정 없이 간단하게 키보드 동작을 제어할 수 있습니다.
1) windowSoftInputMode 기본 설정
AndroidManifest.xml 파일에서 다음과 같이 설정할 수 있습니다.
<activity
    android:name=".MainActivity"
    android:windowSoftInputMode="adjustPan"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
. . . . .
2) 주요 windowSoftInputMode 옵션 상세 분석
windowSoftInputMode 속성에는 여러 옵션이 있으며, 각 옵션은 키보드가 표시될 때 화면의 동작을 결정합니다. 아래 표에서 각 옵션의 효과를 확인할 수 있습니다.
옵션 설명 효과
adjustResize 키보드가 표시될 때 레이아웃의 크기를 조정 키보드가 나타나면 화면 크기가 줄어들고 모든 요소가 재배치됨
adjustPan 키보드가 표시될 때 레이아웃을 위로 밀어올림 현재 포커스된 요소가 항상 보이도록 화면을 이동시킴
adjustNothing 키보드가 표시될 때 레이아웃 변경 없음 키보드가 화면의 일부를 가릴 수 있음
stateHidden 액티비티 시작 시 키보드를 숨김 처음에 키보드가 표시되지 않음
stateAlwaysHidden 항상 키보드를 숨김 입력 필드 터치해도 자동으로 키보드가 나타나지 않음
stateVisible 액티비티 시작 시 키보드를 표시 처음부터 키보드가 나타남
stateAlwaysVisible 항상 키보드를 표시 키보드가 계속 표시됨
. . . . .
3) 옵션 조합 사용법
여러 옵션을 파이프 기호로 조합하여 사용할 수 있습니다. 예를 들어 액티비티 시작 시 키보드를 숨기면서 키보드가 나타날 때는 화면을 밀어올리도록 설정하려면 다음과 같이 작성합니다.
android:windowSoftInputMode="adjustPan|stateHidden"
이 설정은 로그인 화면이나 검색 화면처럼 처음에는 키보드가 필요 없지만 사용자가 입력 필드를 터치하면 키보드가 나타나야 하는 경우에 유용합니다.
. . . . .
4) adjustResize vs adjustPan 선택 가이드
가장 많이 사용하는 두 옵션인 adjustResize와 adjustPan의 차이를 명확히 이해하고 상황에 맞게 선택해야 합니다.
(1) adjustResize를 사용해야 하는 경우
① 입력 필드가 많은 긴 폼 화면에서 ScrollView와 함께 사용할 때
② 화면 전체의 비율을 유지하면서 키보드 영역을 제외한 공간으로 콘텐츠를 재배치해야 할 때
③ 채팅 앱처럼 키보드가 나타나면 메시지 목록이 자동으로 스크롤되어야 할 때
(2) adjustPan을 사용해야 하는 경우
① 단순한 로그인 화면처럼 입력 필드가 몇 개 없고 고정된 레이아웃을 유지해야 할 때
② 화면 전체 크기를 변경하지 않고 현재 포커스된 입력 필드만 보이도록 하고 싶을 때
③ 레이아웃 재계산으로 인한 성능 저하를 피하고 싶을 때

#3. ConstraintLayout으로 키보드 문제 해결하기
AndroidManifest.xml 설정만으로 해결되지 않는 복잡한 레이아웃의 경우 ConstraintLayout을 활용하면 키보드가 나타나도 유연하게 대응할 수 있는 UI를 구성할 수 있습니다.
1) ConstraintLayout의 장점
ConstraintLayout은 상대적 위치 지정을 통해 복잡한 레이아웃을 효율적으로 구성할 수 있으며, 키보드가 나타나 화면 크기가 변경되어도 제약 조건에 따라 자동으로 재배치됩니다.
① 중첩된 레이아웃을 줄여 성능을 향상시킬 수 있음
② 화면 크기 변경 시 제약 조건에 따라 유연하게 대응 가능
③ 키보드가 나타나도 버튼이나 중요한 UI 요소를 화면 하단에 고정시킬 수 있음
. . . . .
2) 로그인 화면 예제 구현
다음은 ConstraintLayout을 사용하여 키보드가 나타나도 로그인 버튼이 항상 화면 하단에 고정되도록 구현한 예제입니다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 상단 컨텐츠 -->
    <TextView
        android:id="@+id/header_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="로그인"
        android:textSize="24sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="50dp" />

    <!-- 중간 입력 필드 -->
    <EditText
        android:id="@+id/username_input"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="사용자 이름"
        android:layout_marginTop="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        app:layout_constraintTop_toBottomOf="@id/header_text"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <EditText
        android:id="@+id/password_input"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="비밀번호"
        android:inputType="textPassword"
        android:layout_marginTop="12dp"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        app:layout_constraintTop_toBottomOf="@id/username_input"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- 하단 버튼 (키보드에 가려질 수 있는 부분) -->
    <Button
        android:id="@+id/login_button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="로그인"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
. . . . .
3) ConstraintLayout 설계 시 핵심 포인트
위 예제에서 중요한 설계 원칙을 정리하면 다음과 같습니다.
(1) 0dp 너비 활용
EditText와 Button의 너비를 0dp로 설정하고 constraintStart_toStartOfconstraintEnd_toEndOf를 parent로 지정하면 화면 크기에 상관없이 좌우 여백을 유지하면서 자동으로 크기가 조정됩니다.
(2) 하단 고정 버튼
로그인 버튼은 layout_constraintBottom_toBottomOf="parent"로 설정하여 항상 화면 하단에 고정됩니다. AndroidManifest.xml에서 adjustResize를 설정하면 키보드가 나타날 때 버튼이 키보드 위로 자동으로 이동합니다.
(3) 입력 필드 연결
첫 번째 EditText는 헤더 텍스트 아래에, 두 번째 EditText는 첫 번째 EditText 아래에 연결되어 순차적으로 배치됩니다. 이렇게 제약 체인을 구성하면 키보드가 나타나도 요소 간 간격이 유지됩니다.

#4. ScrollView 및 프로그래밍 방식의 키보드 제어
입력 필드가 많은 긴 폼이나 복잡한 화면에서는 ScrollView를 활용하고, 필요에 따라 프로그래밍 방식으로 키보드를 직접 제어하는 것이 효과적입니다.
1) ScrollView로 키보드 문제 해결하기
회원가입 폼이나 설문조사 앱처럼 여러 입력 필드가 있는 화면에서는 ScrollView를 사용하여 키보드가 나타나도 모든 콘텐츠에 접근할 수 있도록 해야 합니다.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">

        <!-- 여러 입력 필드들 -->
        <EditText
            android:id="@+id/name_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="이름"
            android:layout_marginBottom="12dp" />

        <EditText
            android:id="@+id/email_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="이메일"
            android:inputType="textEmailAddress"
            android:layout_marginBottom="12dp" />

        <!-- 더 많은 입력 필드 추가 가능 -->

        <Button
            android:id="@+id/submit_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="제출하기"
            android:layout_marginTop="24dp" />

    </LinearLayout>
</ScrollView>
android:fillViewport="true"를 반드시 설정해야 ScrollView가 전체 화면을 채우고, 키보드가 나타나면 자동으로 스크롤이 가능해집니다.
. . . . .
2) 프로그래밍 방식으로 키보드 표시하기
특정 상황에서 코드로 직접 키보드를 표시해야 할 때는 InputMethodManager를 사용합니다.
(1) Java 코드
// 키보드 표시하기
private void showKeyboard(View view) {
    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm != null) {
        view.requestFocus();
        imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
    }
}
(2) Kotlin 코드
// 키보드 표시하기
private fun showKeyboard(view: View) {
    val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
    imm?.let {
        view.requestFocus()
        it.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
    }
}
. . . . .
3) 프로그래밍 방식으로 키보드 숨기기
버튼 클릭 시나 특정 조건에서 키보드를 숨겨야 할 때는 다음과 같이 구현합니다.
(1) Java 코드
// 키보드 숨기기
private void hideKeyboard() {
    View view = getCurrentFocus();
    if (view != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }
}
(2) Kotlin 코드
// 키보드 숨기기
private fun hideKeyboard() {
    currentFocus?.let { view ->
        val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
        imm?.hideSoftInputFromWindow(view.windowToken, 0)
    }
}
. . . . .
4) 키보드 높이 측정하여 UI 조정하기
고급 기법으로 키보드 높이를 실시간으로 측정하여 UI를 동적으로 조정할 수 있습니다. 이는 키보드가 나타날 때 특정 UI 요소를 키보드 위로 이동시키고 싶을 때 유용합니다.
// Kotlin
private fun setupKeyboardHeightObserver() {
    val rootView = findViewById<View>(android.R.id.content)
    rootView.viewTreeObserver.addOnGlobalLayoutListener {
        val rect = Rect()
        rootView.getWindowVisibleDisplayFrame(rect)

        val screenHeight = rootView.height
        val keyboardHeight = screenHeight - rect.bottom

        if (keyboardHeight > screenHeight * 0.15) { // 키보드가 표시됨
            // 키보드 높이에 따라 UI 조정
            bottomContainer.translationY = -keyboardHeight.toFloat()
        } else { // 키보드가 숨겨짐
            bottomContainer.translationY = 0f
        }
    }
}
이 방법은 키보드 높이를 화면 높이의 15% 이상일 때 키보드가 표시된 것으로 판단하여 하단 컨테이너를 키보드 높이만큼 위로 이동시킵니다.

#5. 실전 채팅 앱 UI 구현 및 디바이스별 테스트
채팅 앱은 키보드 관련 문제가 가장 빈번하게 발생하는 앱 유형 중 하나입니다. 실전 예제를 통해 완벽한 채팅 UI를 구현하는 방법을 알아보겠습니다.
1) 채팅 앱 레이아웃 구현
채팅 앱에서는 메시지 목록이 위에 있고 입력창이 항상 하단에 고정되어야 합니다. 키보드가 나타나면 메시지 목록이 자동으로 위로 스크롤되어야 합니다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 채팅 메시지 목록 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_chat"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/layout_chat_input"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <!-- 메시지 입력 영역 -->
    <LinearLayout
        android:id="@+id/layout_chat_input"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <EditText
            android:id="@+id/edit_message"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="메시지 입력..."
            android:padding="12dp"
            android:background="@drawable/bg_rounded_edittext" />

        <ImageButton
            android:id="@+id/button_send"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_marginStart="8dp"
            android:src="@drawable/ic_send"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:contentDescription="메시지 보내기" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
이 레이아웃에서 메시지 입력 영역은 layout_constraintBottom_toBottomOf="parent"로 항상 화면 하단에 고정되고, RecyclerView는 상단부터 입력 영역 위까지 공간을 차지합니다. AndroidManifest.xml에서는 android:windowSoftInputMode="adjustResize"를 설정해야 합니다.
. . . . .
2) 키보드 표시 시 자동 스크롤 구현
키보드가 나타날 때 자동으로 최신 메시지로 스크롤되도록 하려면 다음과 같이 구현합니다.
// 키보드가 나타날 때 자동으로 스크롤을 최신 메시지로 이동
private fun setupKeyboardVisibilityListener() {
    val rootView = findViewById<View>(android.R.id.content)
    rootView.viewTreeObserver.addOnGlobalLayoutListener {
        val rect = Rect()
        rootView.getWindowVisibleDisplayFrame(rect)

        val screenHeight = rootView.height
        val keyboardHeight = screenHeight - rect.bottom

        if (keyboardHeight > screenHeight * 0.15) { // 키보드가 표시됨
            // 마지막 메시지로 스크롤
            recyclerChat.scrollToPosition(adapter.itemCount - 1)
        }
    }
}
. . . . .
3) 디바이스별 테스트의 중요성
서로 다른 Android 기기마다 화면 크기와 키보드 높이가 다르기 때문에 다양한 디바이스에서 테스트하는 것이 매우 중요합니다.
① 작은 화면 스마트폰: 5인치 이하 디바이스에서는 키보드가 화면의 절반 이상을 차지할 수 있으므로 adjustResize 설정이 필수
② 큰 화면 스마트폰: 6인치 이상 디바이스에서는 여유 공간이 많아 adjustPan으로도 충분할 수 있음
③ 태블릿: 화면이 넓어 키보드가 차지하는 비율이 작지만 가로 모드에서는 문제가 발생할 수 있음
④ 다양한 Android 버전: Android 11 이상에서는 WindowInsets API를 활용한 더 정교한 제어가 가능
. . . . .
4) 최종 권장 설정 정리
키보드 문제를 완벽하게 해결하기 위한 최종 권장 설정은 다음과 같습니다.
화면 유형 windowSoftInputMode 레이아웃
단순 로그인 화면 adjustPan ConstraintLayout
긴 회원가입 폼 adjustResize ScrollView + LinearLayout
채팅 앱 adjustResize ConstraintLayout + RecyclerView
검색 화면 adjustPan|stateVisible ConstraintLayout

마무리
Android 앱에서 키보드가 화면 레이아웃에 영향을 주지 않게 하는 방법을 종합적으로 정리하면 다음과 같습니다.
① AndroidManifest.xml 설정: adjustPan은 키보드가 포커스된 영역을 가리지 않도록 화면을 이동하고, adjustResize는 키보드 영역을 제외한 나머지 부분으로 화면 크기를 조정하며, adjustNothing은 키보드가 화면을 가릴 수 있습니다.
② 레이아웃 최적화: ConstraintLayout을 사용하여 유연한 UI를 구성하고, ScrollView를 사용하여 내용을 스크롤 가능하게 만들며, 입력 필드와 중요 버튼을 적절히 배치합니다.
③ 프로그래밍 방식의 제어: InputMethodManager를 사용하여 키보드를 표시하거나 숨기고, 키보드 상태를 감지하여 높이를 측정하며, UI를 동적으로 조정합니다.
④ 실전 적용: 채팅 앱과 같은 복잡한 UI에서는 ConstraintLayout과 adjustResize를 조합하여 사용하고, 다양한 디바이스에서 테스트하여 모든 환경에서 완벽하게 작동하도록 합니다.
이러한 방법들을 조합하여 사용하면 키보드가 나타나도 앱의 사용성을 해치지 않는 완벽한 UI를 구현할 수 있습니다. 사용자 경험을 최우선으로 고려하여 키보드 관련 문제를 해결하는 것이 성공적인 앱 개발의 핵심입니다.
긴 글 읽어주셔서 감사합니다.

끝.
반응형