Android如何优雅的弹出键盘

在很多app中,为了避免ui样式被键盘顶起,从而会把主内容的编辑功能单独摘一个发布器,作为用户的编辑界面,如快手抖音的评论。如下图所示

因此作为发布器必须第一时间弹出输入法,但是过早的调用InputMethodManager.showSoftInput 是无法弹出键盘的,任何的showflag都是一样的,

网上大部分人告诉我们答案,只要将showSoftInput 进行延迟展示即可,更有离谱的告诉你监听draw,监听layout后再调用等

然而这些方法都是不可靠的,监听draw监听layout或者进行delay的话,你会发现换个手机就不生效了,要么就要继续把delay 时间延长保证线上的可靠性,不同手机的延长时间是不一样的。。。。

因此我们本着刨根问底的精神,立即调用不能展示的原因到底是什么。

先说结论,当view已经获得focus和view所在window也获得focus之后,再调用showSoftInput就可以完美展示,注意view的focus和window的focus而且是独立的,但是都可以通过覆写view的方法进行监听

首先看InputMethodManager的showSoftInput

代码块1 InputMethodManager.java

在oncreate中调用,立马发现hasServedByInputMethodLocked 返回了false.导致showSoftInput 返回了false,继续看hasServedByInputMethodLocked

代码块2 InputMethodManager.java

从实现上看checkInputConnectionProxy 所有的view基本都是返回false,延迟后调可以发现是servedView == view,过早调用的时候可以发现servedView是个DecorView而servedView取自ViewRootImpl里的ImeFocusController里的一个成员变量。

ImeFocusController一看这名字就知道是跟输入法焦点控制有关,可以看到,其mServedView赋值是在checkFocus中,而showSoftInput方法里最初是调用了checkFocus(见代码块1)。而在checkFocus中mServedView的值来自mNextServedView

代码块3 ImeFocusController.java

因此我们启动后过早调用showSoftInput的时候应该是mNextServedView为空或者是错误的,所以再看mNextServedView 的赋值过程,如下代码所示

代码块3 ImeFocusController.java

断点后发现,mNextServedView 的赋值过慢,requestFocus并不会使得mNextServedView赋值,原因是hasWindowFocus = false,并且view.hasImeFocus实际上获取的也是当前ImeFocusController.mHasImeFocus的值,这个值的含义主要是表示当前window设置的flag是否支持输入法,需要注意的是即使支持输入法,也需要在window获得focus之后才会变成true(赋值方法在ImeFocusController.onTraversal)

总结一下;

过早调用无法展示的原因是winodow没有获得focus或者是view没有获得focus,因此当window或者focus且view也获得focus后,调用showSoftInput 就不会有问题

即if(view.hasWindowFocus()&&view.hasFocus){ inputManger.showSoftInput(view,0);}

可以通过覆写EditText的onWindowFocusChanged和onFocusChanged,当两个focus都是true的时候再调用showSoftInput 即可