Android 앱을 개발하다 보면 특정 액티비티에서 키보드가 현재 화면에 표시되어 있는지 확인해야 하는 경우가 종종 있습니다. 예를 들어 키보드가 열릴 때 UI 요소 위치를 조정하거나, 특정 동작을 수행하기 전에 키보드 상태를 파악해야 할 수 있습니다.
이 글에서는 ViewTreeObserver와 getWindowVisibleDisplayFrame() 메서드를 활용하여 Android에서 가상 키보드(소프트 키보드)의 표시 여부를 감지하는 방법을 단계별로 살펴보겠습니다.
1단계: 새 프로젝트 생성
Android Studio에서 File → New Project를 선택한 후, 필요한 정보를 모두 입력하여 새 프로젝트를 생성합니다.
2단계: 레이아웃 파일 작성
다음 코드를 res/layout/activity_main.xml에 추가합니다. 화면 상단에는 EditText, 중앙에는 키보드를 숨기는 버튼이 배치됩니다.
<?xml version = "1.0" encoding = "utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android = "https://schemas.android.com/apk/res/android"
xmlns:app = "https://schemas.android.com/apk/res-auto"
xmlns:tools = "https://schemas.android.com/tools"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:id = "@+id/rootview"
tools:context = ".MainActivity">
<EditText
android:id = "@+id/editext"
android:layout_width = "match_parent"
android:layout_height = "wrap_content">
</EditText>
<Button
android:id = "@+id/button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "Click here to hide"
app:layout_constraintBottom_toBottomOf = "parent"
app:layout_constraintLeft_toLeftOf = "parent"
app:layout_constraintRight_toRightOf = "parent"
app:layout_constraintTop_toTopOf = "parent" />
</android.support.constraint.ConstraintLayout>3단계: MainActivity 작성
다음 코드를 src/MainActivity.java에 추가합니다. 핵심은 루트 뷰에 OnGlobalLayoutListener를 등록하여 뷰 계층 구조 변화를 실시간으로 감지하는 것입니다.
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
ConstraintLayout constraintLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
EditText editText=findViewById(R.id.editext);
editText.requestFocus();
constraintLayout=findViewById(R.id.rootview);
constraintLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
constraintLayout.getWindowVisibleDisplayFrame(r);
int screenHeight = constraintLayout.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15) {
Toast.makeText(MainActivity.this,"Keyboard is showing",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this,"keyboard closed",Toast.LENGTH_LONG).show();
}
}
});
button.setOnClickListener(this);
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
hideSoftkeybard(v);
break;
}
}
private void hideSoftkeybard(View v) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}코드 핵심 로직 설명
위 코드에서는 ViewTreeObserver 리스너를 사용하여 해당 액티비티에서 발생하는 뷰 그룹의 변화를 감지합니다. 이 옵저버 안에서 다음 코드로 루트 엘리먼트의 전체 높이를 구할 수 있습니다.
int screenHeight = constraintLayout.getRootView().getHeight();
그다음, 아래와 같이 키보드 높이를 계산합니다. 화면에서 실제로 보이는 영역의 하단 좌표를 전체 화면 높이에서 빼면 키보드가 차지하는 높이가 나옵니다.
Rect r = new Rect(); constraintLayout.getWindowVisibleDisplayFrame(r); int keypadHeight = screenHeight - r.bottom;
마지막으로 계산된 키보드 높이를 화면 전체 높이와 비교합니다. 일반적으로 키보드는 화면 높이의 15% 이상을 차지하므로, 이 값을 기준으로 표시 여부를 판단합니다.
if (keypadHeight > screenHeight * 0.15) {
Toast.makeText(MainActivity.this,"Keyboard is showing",Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this,"keyboard closed",Toast.LENGTH_LONG).show();
}4단계: AndroidManifest.xml 설정
아래와 같이 AndroidManifest.xml 파일에 코드를 추가합니다.
<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "https://schemas.android.com/apk/res/android"
package = "com.example.andy.myapplication">
<application
android:allowBackup = "true"
android:icon = "@mipmap/ic_launcher"
android:label = "@string/app_name"
android:roundIcon = "@mipmap/ic_launcher_round"
android:supportsRtl = "true"
android:theme = "@style/AppTheme">
<activity android:name = ".MainActivity"
android:windowSoftInputMode = "stateAlwaysVisible">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>위 코드에서는 windowSoftInputMode 속성을 stateAlwaysVisible로 설정했습니다. 이렇게 하면 EditText가 포커스를 받는 즉시 키보드가 자동으로 나타나므로, 키보드 감지 기능을 바로 테스트하기에 좋습니다.
앱 실행 및 결과 확인
이제 애플리케이션을 실행해 보겠습니다. 실제 Android 모바일 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤, 툴바에서
실행 아이콘을 클릭하세요. 목록에서 본인의 모바일 기기를 선택하면, 기기 화면에 앱의 기본 화면이 표시됩니다.

위 출력 결과에서 키보드가 화면에 표시되면 "Keyboard is showing"(키보드가 표시 중입니다)이라는 메시지가 토스트로 나타납니다.

반대로 버튼을 눌러 키보드를 숨기면 위 화면과 같이 "Keyboard closed"(키보드가 닫혔습니다)라는 메시지가 표시되는 것을 확인할 수 있습니다.
마무리
이처럼 ViewTreeObserver.OnGlobalLayoutListener와 getWindowVisibleDisplayFrame()을 조합하면 Android에서 소프트 키보드의 표시 여부를 간단하게 감지할 수 있습니다. 이 방법은 키보드 등장 시 화면 요소를 재배치하거나, 채팅 앱처럼 키보드 상태에 따라 UI를 동적으로 조정해야 하는 경우에 매우 유용하게 활용됩니다.