-
안드로이드 EditText 자동으로 가상키보드가 없어지지 않는 문제카테고리 없음 2020. 10. 9. 21:18
(현재 글의 코드 예시는 코틀린으로 작성되었습니다.)
EditText를 눌러서 편집모드에 들어가면 가상 키보드가 자동으로 생성.
바깥 영역을 누르면 자동으로 키보드가 들어갔으면 좋겠는데 그렇지 않음.
시도1. 아래의 자료를 참고하였다.
https://stackoverflow.com/questions/15412943/hide-soft-keyboard-on-losing-focus
결론부터 말하면 첫 번째 시도는 실패했다.
아래는 첫 번째 시도한 코드이다. 실패했다고는 하지만 이 코드에서 조금만 추가하면 바로 성공하는 코드를 만들 수 있으니 이 코드를 먼저 살펴보자.
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) et1.setOnFocusChangeListener { v, hasFocus -> if (!hasFocus) { val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(v.windowToken, 0) } } } }
(코틀린 익스텐션을 이용해 findViewById(R.id.~~)는 생략하였다.)
et1은 키보드 테스트를 할 EditText의 아이디이다.
et1에 set 해준 OnFocusChangeListener의 내용을 (대충 필자 지식 범위 안에서) 해석하자면,
현재 뷰가 focus를 갖고 있지 않으면 SoftInput을 hide하라는 내용인 것 같다. 여기서 SoftInput은 가상 키보드를 의미한다.
그러나 이 코드 만으로는 먹히지 않는다. 왜인지 알고싶다면 로그를 찍어보자.
로그를 찍기 위해 위의 코드에서 딱 세 줄을 추가하였다. (TAG라는 단어가 들어가는 세 줄이 추가된 줄이다.)
class MainActivity : AppCompatActivity() { private val TAG = "MyLog" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) et1.setOnFocusChangeListener { v, hasFocus -> Log.d(TAG, "et1 focus change listener") if (!hasFocus) { Log.d(TAG, "hasFocus false") val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(v.windowToken, 0) } } } }
그랬더니...
리스너가 호출되긴 한다. 다만, 잉여잉여한 흰 배경을 클릭할 때가 아니라, 화면에 있는 다른 EditText를 클릭할 때만 호출된다.
그래서야 1번 EditText가 키보드를 접으면 2번 EditText가 포커스를 얻어 다시 키보드를 보여주기 때문에 가상 키보드는 절대 없어지지 않는다.
결국 문제는 어떻게 잉여한 흰 바탕이 포커스를 가질 수 있게 하느냐는 것인데.......
시도2. 아래의 답변을 참고, 이번엔 성공하였다.
필자가 참고한 답변은 여기서 베스트로 간택받은 답변 바로 밑에 있는 답변이다. 본인이 영어가 된다 싶으면 위 링크의 질문 밑에 베스트답변 밑에 있는 답변을 읽어볼 것. (어차피 코드는 만국공통이라 영어 좀 몰라도 되긴 한다.)
핵심은 역시나 바탕이 포커스를 가질 수 있게 하는 것인 듯 하다.
아까 실패한 첫 번째 코드에다가 아래의 xml설정만 더해주면 바로 된다.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout // 가독성을 위해 중요하지 않은 부분 생략 android:layout_width="match_parent" android:layout_height="match_parent" android:clickable="true" android:focusableInTouchMode="true" > <EditText android:id="@+id/et1" // 생략... /> <EditText android:id="@+id/et2" // 생략... /> </androidx.constraintlayout.widget.ConstraintLayout>
위의 xml코드는 가독성을 위해 필수 요소들을 생략했으니 그대로 복붙하면 당연히 돌아가지 않는다. 참고만 할 것.
Layout은 ViewGroup의 일종이고, ViewGroup은 View의 일종이고, View는 포커스를 가질 수 있다.
잘 모르겠다면 안드로이드 공식 자료를 보자.
https://developer.android.com/reference/android/view/ViewGroup
그리고, 하얗게 잉여잉여해 보인 부분은 실은 공백이 아니라 레이아웃이다. (혹시 최상위 레이아웃을 wrap_content로 해놨다면 모를까.)
그러므로 최상위 레이아웃에 clickable, focusableInTouchMode 설정 두 가지를 하면 흰 배경부분이 포커스를 가질 수 있게 되면서 OnFocusChangeListener가 정상적으로 호출된다!
아래 영상을 통해 여백을 눌렀을 때 가상키보드가 접히는 것을 확인할 수 있다.
======================================================================
모르고 넘어가도 되고 필자도 이유는 잘 모르겠지만 흥미로운 이야기:
혹시 화면에 버튼을 집어넣은 경우, xml에서 특별한 설정을 하지 않는 한 버튼은 포커스를 갖지 못하는 듯 하다.
그럴 때는 버튼에도 android:focusableInTouchMode="true" 설정을 해주면 버튼을 클릭할 때도 EditText의 가상키보드를 접을 수 있다.
TextView는 별다른 설정 없이 부모레이아웃의 설정을 그대로 적용받는 듯 하다.
혹시나 싶어서 TextView에도 setOnFocusListener를 하고 TextView를 클릭해보았는데, TextView의 리스너는 절대로 invoke되지 않는다. TextView의 위치를 누르면 Layout의 리스너가 invoke된다. 이유는 잘 모르겠다.