1.前言如果有使用输入的需求,通常会有自动弹出或关闭软键盘的需求。开篇就清楚了,本文将讲一下弹出和关闭软键盘的一些细节,最后从源码上分析一下。操作软键盘需要用到InputMethodManager,它是一个系统服务,可以使用Context.getSystemService()获取。而很多关键的逻辑代码都是在InputMethodManagerService中实现的。特别说明:本文分析的所有源码均基于Android26源码。2.操作软键盘2.1InputMethodManager前面提到,如果要操作软键盘,需要用到InputMethodManager,它是一个系统服务。如果你想获取它,你可以使用getSystemService()来获取它。毕竟是系统服务。为了安全,在使用的时候,还是要判断null,避免空指针。2.2显示软键盘在InputMethodManager中,有两个方法showSoftInput()和showSoftInputFromInputMethod(),但实际上只有showSoftInput()有效。它有两个重载方法,通常我们会使用它的双参数方法。这里我们只需要传递两个参数。它首先需要一个View,而软键盘的使用是为了输入,输入需要一个接收输入内容的View。这里接收输入的View肯定是一个EditText(但不是必须的)。第二个参数flags是标志位。从上面截图中方法签名的文档可以看出,它接收的是0或者SHOW_INPYT_IMPLICIT两个参数,但实际上它还有第三个参数,另一个是SHOW_FORCED。可见1和2有特殊的含义。其实它们不影响显示,只是隐藏时有一些限制。这些我们以后看源码的时候再说。一般如果没有特殊需要,我们直接传0就可以了。现在简单总结一下调用showSoftInput()时会生效的要点:1、第一个参数,***是EditText或者它的子类。考虑到软键盘是用来输入的,EditText就是一个接收输入的控件。而且这也不是绝对的,如果不是EditText,就必须要求这个View有两个属性,即:android:focusable="true"和android:focusableInTouchMode="true"。2.第一个参数必须是focusable并且已经被聚焦。默认情况下是允许EditText获取焦点的,但是如果布局中有多个控件可以获取焦点,我们就需要让我们传入的View提前获取焦点。要获得焦点,请使用requestFocus()方法。3.必须加载布局。在onCreate()中,立即调用showSoftInput()不会生效。如果想在页面一启动就弹出键盘,可以在Activity上设置android:windowSoftInputMode属性来完成,或者做一个延迟加载,View.postDelayed()也是一种解决方法。所以最后,完整的显示软键盘的代码如下。2.3隐藏软键盘showSoftInput()方法虽然有效,但是如果要隐藏软键盘,没有对应的hideSoftInput()方法,但是有一个hideSoftInputFromWindow()方法可以用来隐藏软键盘。我们先来看看这个方法的签名。它还有两个可以调用的方法。它接收两个参数,第一个参数是一个IBinder,可以直接传一个View.getWindowToken()的windowToken对象。第二个参数是隐藏软键盘的标志。如果没有特殊要求,直接传0即可。注意,虽然原则上在传递之前需要传递一个弹出键盘时传递的View的windowToken,但实际情况是只需要传递一个存在于当前布局ViewTree中的任意View的windowToken即可。最终隐藏软件的代码就是这样。2.4切换键盘的弹出和隐藏在InputMethodManager中,还提供了一个toggleSoftInput()方法,顾名思义,它允许软键盘在显示和隐藏之间切换。该方法接收两个标志,分别是控制显示和隐藏时的标志。它们的含义与前面介绍的showSoftInput()和hideSoftInputFromWindow()是一致的,所以没有特殊要求,直接传0即可。toggleSoftInput()方法不需要传递View或者windowToken,所以它没有showSoftInput()中的一些限制,但是还是需要在layout绘制完成后调用才会有效果。这种方法虽然限制很少,但我们基本不会用。主要是因为它是一个switch方法,会根据当前状态做相反的操作。这就导致很多时候,在代码中,我们无法直接根据InputMethodManager提供的方法来判断当前软键盘的显示状态,从而无法判断调用时的效果。3.源码分析3.1Flag细节前面的一些方法需要传递一个flag值,文档中没有详细说明。下面我们从源码的角度来分析一下这几个标志位的含义。让我们首先看一下showSoftInput()方法。它最终会调用mService.showSoftInput()方法。最终的源码需要查看InputMethodManagerService中的代码。showSoftInput()方法最终会调用showCurrentInputLocked()。这个方法的代码很长,我们只关心flag相关的代码。可以看出flag会影响mShowExplicitlyRequested和mShowForced两个字段,SHOW_FORCED会更强大。hideSoftInputFromWindow()方法最终会调用InputMethodManagerService中的hideCurrentInputLocked()方法。从DEBUG==true会输出的Log,已经可以看出意思了。这里传递的两个flag会根据显示和隐藏进行比较。也就是说,如果flag使用不当,可能会导致这里直接返回false,从而无法隐藏软键盘。这些细节和代码对比就会一目了然,所以文章中不在此处重复这些细节。所以这就是为什么前面说了,如果没有特殊要求,直接传0就可以了,可以绕过这个限制。3.2如何判断软键盘是否弹出由于toggleSoftInput()可以根据软键盘的当前状态进行不同的操作,所以必须有一种方法来判断当前软键盘的状态。然后我们继续跟踪toggleSoftInput()方法的源码。该方法最终会调用InputMethodService的onToggleSoftInput()方法。该方法中通过isInputViewShow()方法判断当前软键盘是否处于弹出状态。但是我们没有办法直接和InputMethodService交互,也没有办法直接获取当前键盘是否显示。如果要监听键盘的弹出和关闭,可以使用ViewTreeObserver.OnGlobalLayoutListener来监听布局的调整,从而判断键盘的弹出和隐藏。我们将有时间讨论这些细节。4、既然KeyboardUtils已经清楚了软键盘如何关闭和弹出的细节,下面我们来写一个helper类来解决这个问题。让你得到它并使用它。这是Java和Kotlin版本。4.1Java版publicclassKeyboardUtils{publicstaticvoidshowKeyboard(Viewview){InputMethodManagerimm=(InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);if(imm!=null){view.requestFocus();imm.showSoftInput(view,0);}}publicstaticvoidhideKeyboard(Viewview){InputMethodManagerimm=(InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);if(imm!=null){imm.hideSoftInputFromWindow(view.getWindowToken(),0);}}publicstaticvoidtoggleSoftInput(Viewview){InputMethodManagerimm=(InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);if(imm!=null){imm.toggleSoftInput(0,0);}}}4.2Kotlin版objectKeyboardktUtils{funshowKeyboard(视图:View){valimm=view.context.getSystemService(Context.INPUT_METHOD_SERVICE)asInputMethodManagerif(imm!=null){view.requestFocus()imm.showSoftInput(view,0)}}funhideKeyboard(view:View){valimm=view.context.getSystemService(Context.INPUT_METHOD_SERVICE)asInputMethodManagerimm?.hideSoftInputFromWindow(view.windowToken,0)}funtoggleSoftInput(view:View){valimm=view.context.getSystemService(Context.INPUT_METHOD_SERVICE)asInputMethodManagerimm?.toggleSoftInput(0,0)}}
