EditText 외부 영역 터치 시 키보드 숨기기
이 글에서는 안드로이드 EditText의 외부 영역을 터치했을 때 소프트 키보드를 숨기는 방법에 대해 소개한다.
안드로이드에서 EditText의 외부 영역을 터치했을 때 키보드가 아래로 사라지는 기능은 이미 많은 앱에서 제공하는 익숙한 기능이며, 앱의 사용자 또한 당연히 이렇게 동작 할 것이라고 생각한다. 하지만 이 기능은 안드로이드 내부적으로 제공되는 기능이 아니기 때문에 개발자가 따로 구현할 필요가 있다.
많이 사용되는 기능인 만큼 조금만 검색하여도 해당 기능을 어떻게 구현할 수 있는지에 대해 많은 정보를 얻을 수 있다. 화면의 루트 레이아웃에 키보드를 숨기는 클릭 리스너를 추가하는 방법도 있고, 포커스를 전달하여 키보드를 숨기는 방법도 찾을 수 있지만 내가 원하는 것은 XML 또는 Activity마다 추가적인 코드를 작성할 필요 없이 일반적으로 적용 가능한 방법이었다.
일반적인 구현 방법
구글에 키보드를 숨기는 기능에 대해 검색했을 때 쉽게 찾을 수 있는 코드를 가져왔다. 여기서는 Activity의 dispatchTouchEvent를 오버라이드하고 현재 포커스를 가지고 있는 뷰의 터치 영역과 이벤트가 발생한 좌표를 비교하여 소프트 키보드를 숨기고 있다.
이 코드는 단순한 상황이라면 문제 없이 동작한다. 하지만 일부 상황에서 기대하는것 처럼 동작하지 않는 문제가 발생하여 다른 방법을 찾아야만 했다.
문제점 1: 다른 EditText 선택 시 키보드 재등장
위의 코드의 첫 번째 문제점은 화면에서 EditText를 여러 개 사용할 때, 다른 EditText를 선택할 때 마다 키보드가 사라졌다가 다시 등장하는 것이다.
문제가 되는 상황을 구현하면 아래와 같다.
문제점 2: 스크롤 뷰에서 이상 동작
위의 코드를 보면 터치 이벤트의 액션이 ACTION_UP이거나 ACTION_MOVE인 경우 키보드를 숨기게 된다. 때문에 스크롤 뷰 내에서 EditText를 사용할 때 원하는데로 동작하지 않는 문제가 발생하였다.
EditText를 선택하고 스크롤 수행 시 키보드가 자동으로 닫히게 된다. 조건에서 ACTION_MOVE를 제거하더라도 문제는 동일하다. 스크롤이 끝나고 손가락을 떼었을 때 키보드가 자동으로 닫히게 된다.
이것은 내가 원하는 동작이 아니었다. 키보드가 등장하면서 화면의 높이가 줄어드는 만큼 키보드를 열어둔 채 화면을 스크롤하여 좀 더 아래에 있는 EditText를 선택하고 싶었다.
이 문제를 해결하기 위해서는 기존의 코드에서 키보드가 닫히는 조건을 변경할 필요가 있다.
해결 방안
문제점을 해결할 수 있었던 코드는 아래와 같다.
- 단순히 ACTION_UP 이벤트에서 키보드를 숨기고 싶지 않았기 때문에 SingleTapUp 제스처를 사용하였다.
- 다른 EditText를 터치했을 때 키보드를 가리지 않기 위해서는 기존의 포커스를 가지고 있던 뷰를 저장해야 하였고, prevFocus라는 프로퍼티를 추가하였다.
- dispatchTouchEvent에서는 ACTION_UP 이벤트에서 현재 포커스를 가지고 있는 뷰를 저장한다.
- onSingleTapUp 리스너에서는 먼저 이전에 포커스를 가지고 있던 뷰의 터치 영역을 계산하고, 현재 터치한 영역이 prevFocus의 터치 영역 밖일 때 키보드를 숨겨야할 지 결정한다.
- 현재 포커스를 가지고 있는 뷰가 EditText이면서 이전에 포커스를 가지고 있는 뷰와 다른 뷰라면, 다른 EditText를 선택한 것이므로 리스너를 종료한다.
- 현재 포커스를 EditText가 아닌 다른 뷰에서 가지고 있다면 당연히 키보드를 숨겨야 하며, 포커스가 변경되지 않았다면 EditText를 선택한 상태에서 외부 영역을 터치한 것이므로 키보드를 숨긴다.
끝.