프로젝트를 진행하면서 키보드 문제에 스트레스를 받아왔기에 후기를 남겨본다.
일단, 나의 레이아웃 구조는 이렇다.
나는 웹뷰 내의 인풋 박스가 포커스를 받을 때, 즉, 소프트 키보드가 올라왔을때 하단의 button을 Gone 처리를 하고 싶었다.
그리고, 키보드가 내려갔을 때는 Visible 처리를 하고 싶었다.
바로 든 생각은 키보드에 의해서 화면이 Resize가 되면서, 기존 크기와 비교하여 키보드가 올라왔는지, 내려갔는지 판별후 콜백 메소드를 호출해주는 방식이었다.
그렇게 나는 KeyboardVisibilityUtils 클래스를 만들었다.
먼저 KeyboardVisibilityUtils의 람다 호출 메소드 onShowKeyboard와 onHideKeyboard가 호출될 때, 엑티비티로 호출해줄 인터페이스가 필요했다. 그렇기에 아래와 같은 OnChangeKeyboardListener 인터페이스를 만들었다.
[OnChangeKeyboardListener.kt]
interface OnChangeKeyboardListener {
/**
* 키보드가 보여질 때
*/
fun onShowKeyboard()
/**
* 키보드가 숨겨질 때
*/
fun onHideKeyboard()
}
그리고 OnChangeKeyboardListener의 구현 부분을 activity 클래스내에 선언했다.
[WebviewActivity.kt]
class WebviewActivity : AppCompatActivity(), OnChangeKeyboardListener{
...
/**
* 키보드가 보여졌을 때의 이벤트 처리
*/
override fun onShowKeyboard() {
Log.d("debugTest", "하단버튼을 없앤다.")
viewDataBinding.run {
btnFooterExam.visibility = View.GONE
viewFooterExam.visibility = View.GONE
}
}
/**
* 키보드가 숨겨졌을 때의 이벤트 처리
*/
override fun onHideKeyboard() {
Log.d("debugTest", "하단버튼을 생성한다.")
viewDataBinding.run {
btnFooterExam.postDelayed({
btnFooterExam.visibility = View.VISIBLE
viewFooterExam.visibility = View.VISIBLE
}, 50)
}
}
}
구현체 부분에는 키보드가 올라올 때 하단 뷰를 없애고, 키보드가 내려갈 때 하단 뷰를 생성되게 하도록 구현했다.
이렇게 만들어진 구현체 부분을 WebviewFragment의 addKeyboardListener() 메소드를 통해서 넘겨줬다.
[WebviewFragment.kt]
....
//키보드가 올라왔는지 내려갔는지 확인후 엑티비티에 알려주는 리스너
private var keyboardListener: OnChangeKeyboardListener? = null
/**
* [keyboardListener]를 등록한다.
*
* @param keyboardListener 키보드의 상태를 전달하는 리스너
*/
fun addKeyboardListener(keyboardListener: OnChangeKeyboardListener) {
this.keyboardListener = keyboardListener
}
....
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//키보드 가시성 유틸 초기화
keyboardVisibilityUtils =
KeyboardVisibilityUtils(activity!!.window,
onShowKeyboard = {
keyboardListener?.onShowKeyboard()
},
onHideKeyboard = {
keyboardListener?.onHideKeyboard()
}
)
}
그렇게 추가한 keyboardListener의 두 메소드는
KeyboardVisiblityUtils에 의해서 람다 onShowKeyboard 메소드가 호출될때 onShowKeyboard()를 호출하도록 했고. 람다 onHideKeyboard 메소드가 호출되면서 onHideKeyboard()를 호출하도록 구현했다.
[KeyboardVisibilityUtils.kt]
class KeyboardVisibilityUtils(
private val window: Window,
private val onShowKeyboard: ((keyboardHeight: Int) -> Unit)? = null,
private val onHideKeyboard: (() -> Unit)? = null
) {
private val MIN_KEYBOARD_HEIGHT_PX = 150
private val windowVisibleDisplayFrame = Rect()
//마지막으로 보여진 전체 뷰 세로 길이
private var lastVisibleDecorViewHeight: Int = 0
private val onGlobalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
//실제로 사용자가 볼 수 있는 디스플레이 크기를 가져온다.
window.decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame)
//현재 보여지는 디스플레이 크기의 세로 길이를 할당한다.
val visibleDecorViewHeight = windowVisibleDisplayFrame.height()
Log.d("debugTest", "visibleDecorViewHeight : $visibleDecorViewHeight")
//마지막으로 정했던 디스플레이 크기가 있어야하고
if (lastVisibleDecorViewHeight != 0) {
//그 마지막으로 정해졌던 디스플레이 크기와 비교했을 때 지금 현재 디스플레이 크기보다 전 디스플레이 크기가 크다면 키보드가 올라왔다고 판단한다.
if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
val currentKeyboardHeight = window.decorView.height - windowVisibleDisplayFrame.bottom
Log.d("debugTest","decorView height : ${window.decorView.height}, 현재보여지고 있는 크기의 가장 아래 : ${windowVisibleDisplayFrame.bottom}")
//키보드가 올라왔다는 리스너 호출
onShowKeyboard?.invoke(currentKeyboardHeight)
} else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
//키보드가 내려갔다는 리스너 호출
onHideKeyboard?.invoke()
}
}
// 다음에 불릴 때 사용하기 위해서 현재 디스플레이 크기의 세로 길이를 저장해둔다.
lastVisibleDecorViewHeight = visibleDecorViewHeight
}
init {
window.decorView.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
}
fun detachKeyboardListeners() {
window.decorView.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener)
}
}
KeyboardVisibilityUtils에서는 전체 크기의 변화를 탐지할 수 있도록, viewTreeObserver의 onGlobalLayoutListener를 사용했고, detachKeyboardListeners()가 선언되기 전까지 전체 크기의 변화를 탐지하여 람다 onShowKeyboard 메소드 와 람다 onHideKeyboard 메소드를 호출했다.
그 결과..
키보드가 내려갈때 하단 버튼이 생기는 과정은 아주 매끄럽게 진행되었다.
하지만, 키보드가 올라올때 하단 버튼이 사라지긴 하지만, 깜빡이면서 사라지는 느낌이 강했다.
그래서 생각했다. webview의 InputBox에서 focus를 받는 그 순간 앱쪽으로 focus 받았다고 메소드를 바로 호출해주면 키보드가 올라오기 전 처리 되지 않을까?? 그래서 한번 해보려고한다.
webview에서 호출값을 받아서 처리하는 부분은 webview에서 키보드 상태에 따라서 이벤트 처리하기 -2에서 소개해보도록 하겠다.