Android显示和隐藏软键盘方法

InputMethodManager类

Android中软键盘的管理主要是通过InputMethodManager类来完成的。 

InputMethodManager对象的获取方法如下:

获取到InputMethodManager对象后就可以通过调用其成员方法来对软键盘进行操作。不过在使用InputMethodManager对象前通常都需要判断其是否为null,避免运行时异常:

显示软键盘

Android中可以通过InputMethodManager的showSoftInput()方法来显示软键盘。 

InputMethodManager的showSoftInput()方法原型为:

它有两个参数,第一个参数表示当前要接收软键盘输入的view,第二个参数是软键盘显示时的控制参数。使用InputMethodManager的showSoftInput()方法来显示软键盘有如下注意事项:

        1.第一个参数中view必须是EditText,或者EditText的子类,如果是其他类型的View、如Button、TextView等,showSoftInput()方法不起作用。

        2.第一个参数中的view必须是可以获取焦点的(即view.isFocusable()返回true),如果不能获取焦点,则showSoftInput()方法不起作用。EditText默认是可获取焦点的,所以此条件一般都可以满足。如果不满足,可以通过view.setFocusable(true);将其设置为可获取焦点的view。

        3.第一个参数中的view当前必须已经获取到焦点(即view.isFocused()返回true),如果当前焦点不在该view上,则showSoftInput()方法不起作用。虽然EditText默认是可获取焦点的,但由于一个布局中可能会有多个控件可以获取焦点,焦点位置不一定会恰好在EditText上,所以此条件不一定满足。为了让showSoftInput()可以起作用,必须在之前showSoftInput()前先通过view.requestFocus()获取焦点。然后再执行showSoftInput()。

        4.第一个参数中的view必须是可见的,即view.getVisibility()等于View.VISIBLE,如果view是不可见的,无论view.getVisibility()是View.INVISIBLE还是View.GONE,showSoftInput()方法都不起作用。如果view是不可见的,可以先通过view.setVisibility(View.VISIBLE)将其设置为可见。

        5.当前布局必须已经完成加载,如果还未绘制完成,则showSoftInput()方法不起作用。特别的,在Activity的onCreate()中执行showSoftInput()是不起作用的。如果要再布局文件加载后就显示软键盘,可以通过postDelayed的方式来延迟执行showSoftInput()。延迟时间不能太短,一般要在50ms以上。代码示例如下:

        6.InputMethodManager类中提供了另外一个显示软键盘的方法showSoftInputFromInputMethod,和showSoftInput不同的是,第一个参数传入的不是一个View对象,而是一个View对象的windowToken(对一个view对象可以通过getWindowToken()的方法获取到其windowToken。不过实际情况是,即使上述所有条件都满足,通过showSoftInput(view,flag)已经可以显示软键盘,但是通过showSoftInputFromInputMethod(view.getWindowToken(), flag)仍然无法显示软键盘。

        7.参照Android官方文档,第二个参数提供一些额外的操作标记(additional operating flags),可以取0或者SHOW_IMPLICIT,0表示什么含义没有说明,SHOW_IMPLICIT表示本次显示软键盘的请求不是来自用户的直接请求,而是隐式的请求。且不说一会用数字,一会用常量名,光SHOW_IMPLICIT的说明恐怕除了这个接口的开发人员,没人能看懂这句话是什么意思。实际上这个参数还可以取第三个值SHOW_FORCED,直接在文档中被遗忘了。经过试验,这个参数的取值对软键盘的显示没有任何影响,无论取哪一个值软键盘都能够正常显示(即使随便输入一个整数,软键盘都可以显示)。实际上这个参数影响的并不是软键盘的显示,而是软键盘的隐藏。

隐藏软键盘

Android在InputMethodManager类中并没有提供一个和showSoftInput()对应的hideSoftInput()方法来隐藏软键盘,而是提供了一个showSoftInputFromInputMethod()相对应的hideSoftInputFromWindow()方法来隐藏软键盘。幸运的是,虽然showSoftInputFromInputMethod()不能正常显示软键盘,hideSoftInputFromWindow()倒是能够隐藏软键盘。

InputMethodManager的hideSoftInputFromWindow()方法原型为:

它同样有两个参数,第一个参数是一个View的windowToken,第二个参数是软键盘隐藏时的控制参数。使用 InputMethodManager的hideSoftInputFromWindow()方法来隐藏软键盘有如下注意事项:

        1.第一个参数并不是指定一个View,而是一个View的windowToken。对一个view可以通过getWindowToken()的方法获取到其windowToken。

        2.按照官方文档,第一个参数中的windowToken应当是之前请求显示软键盘的View的windowToken,也就是执行showSoftInput()时第一个参数中的View的windowToken。但是实际情况是,用任意一个当前布局中的已经加载的View的windowToken都可以隐藏软键盘,哪怕这个View被设置为INVISIBLE或GONE。因此,如果不知道之前是谁请求显示的软键盘,可以随便传入一个当前布局中存在的View的windowToken。特别的,可以传入一个Activity的顶层View的windowToken,即getWindow().getDecorView().getWindowToken(),来隐藏当前Activity中显示的软键盘,而不用管之前调用showSoftInput()的究竟是哪个View,示例代码如下:

这里还要注意的是,可以随便传入一个当前布局中存在的View的windowToken,并不代表可以传入任意一个View的windowToken,如下代码不能实现隐藏软键盘:

对新创建的view,必须将其加入到当前布局中后才可以用来隐藏软键盘。如下代码可以实现隐藏软键盘:

        3.参照Android官方文档,第二个参数同样是提供了一些额外的操作标记(additional operating flags),可以取0或者HIDE_IMPLICIT_ONLY,0表示什么含义同样是没有说明,HIDE_IMPLICIT_ONLY表示当前的软键盘应当只在其不是被用户显式的显示的时候才隐藏(the soft input window should only be hidden if it was not explicitly shown by the user)。这句话虽然很拗口,但总算是有点有用的信息了。在显示软键盘时,可以使用的flag有0,SHOW_IMPLICIT和SHOW_FORCED,参照之前的描述,当显示软键盘指定flag为SHOW_IMPLICIT时表示隐式的显示,也就是这里非用户显式的显示,再参照这里的描述,如果隐藏软键盘时使用的flag为HIDE_IMPLICIT_ONLY,那么软键盘只有在非用户显式的显示的时候才隐藏,这意味着如果隐藏软键盘时使用的flag为HIDE_IMPLICIT_ONLY,那么只有当显示软键盘时指定的flag为SHOW_IMPLICIT时,软键盘才会隐藏,如果显示软键盘时指定的flag不是SHOW_IMPLICIT,而是0或者SHOW_FORCED,那么软键盘就不会隐藏。为了更完整的看出不同flag对隐藏软键盘的影响(再声明下,无论是显示软键盘时指定的flag,还是隐藏软键盘时指定的flag都只对隐藏软键盘有影响,对显示软键盘无影响), 分别在调用showSoftInput()时使用三个不同的标记,以及在调用hideSoftInputFromWindow()是使用三个不同的标记(隐藏软键盘时同样还有一个HIDE_NOT_ALWAYS标记,它同样在文档中被遗忘了),对是否能够隐藏软键盘进行测试,测试结果如下:

(T表示可以隐藏,F表示不能隐藏)

可以看到,在隐藏软键盘时使用HIDE_IMPLICIT_ONLY标记,确实只有在显示软键盘时使用SHOW_IMPLICIT时才会隐藏。此外,当隐藏软键盘时使用0作为标记,无论showSoftInput()时使用的是哪个参数,都可以隐藏软键盘。

切换软键盘状态

在InputMethodManager类中还提供了一个toggleSoftInput()的方法来在显示和隐藏软键盘之间切换,也就是说,如果当前软键盘是隐藏的,那么执行toggleSoftInput()方法时会显示软键盘,如果当前软键盘是显示的,那么执行toggleSoftInput()方法时会隐藏软键盘。

InputMethodManager的toggleSoftInput()方法原型为:

它同样有两个参数,第一个参数是显示软键盘时使用的标记,第二个参数是隐藏软键盘时使用的标记。使用InputMethodManager的toggleSoftInput()方法来切换软键盘显示状态有如下注意事项:

        1.showFlags为显示软键盘时使用的标记,只有当前软键盘处于隐藏状态时才会使用。hideFlags是隐藏软键盘时使用的标记,只有当前软键盘处于显示状态时才会使用。

        2.showFlags和hideFlags取值范围,以及不同取值的影响和上文分析的一样。即showFlags和hideFlags都只影响软键盘的隐藏,不影响软键盘的显示。不同取值对软键盘隐藏的影响参见上文中的表格。

        3.和showSoftInput()方法不同的是,使用toggleSoftInput()显示软键盘时,并不要求当前界面布局中有一个已经获取焦点的EditText,即使当前布局是完全空白的,一个View也没有(除了最外层的Layout),toggleSoftInput()也能够显示软键盘。不过如果没有一个已经获取焦点的EditText,那么软键盘中的按键输入都是无效的。就像这样。

        4.显示软键盘时,要求当前布局必须已经加载完成,如果还未绘制完成,则toggleSoftInput()方法不起作用。这点和之前调用showSoftInput()显示软键盘时描述的第5点要求是一样的。特别的,在Activity的onCreate()中执行toggleSoftInput()必须通过postDelayed的方式来延迟执行。延迟时间一般要在50ms以上。

        5.当隐藏软键盘时,不需要知道之前触发软键盘显示的View是哪一个或获取当前布局中任何一个View的windowToken,只要hideFlags能够隐藏就可以。由于hideFlags为0时总是能够隐藏的,因此,使用toggleSoftInput(0, 0)应当是最方便的无条件隐藏软键盘的方法,前提是知道当前软键盘确实是处于显示状态。不过遗憾的是Android没有任何API可以直接获取到软键盘的状态是显示还是隐藏的。

部分源码解读

显示和隐藏软键盘的源码大都在InputMethodManagerService.java文件里。这个文件有3000多行代码,还有很多是其他类的交互,我也没有仔细研究,这里只是将其在一些片段贴出来,做一个大概的分析:

    1.showSoftInput 

    showSoftInput()会进入到showCurrentInputLocked()方法中,这里有这样一段:

可以看到这里只是将showSoftInput的第二个参数flag和SHOW_IMPLICIT,SHOW_FORCED相与,根据结果对mShowExplicitlyRequested和mShowForced赋值。在showCurrentInputLocked()方法没有其他用到flags的地方,也没有用到mShowExplicitlyRequested和mShowForced。这就解释了,执行showSoftInput()时传入任意的flags都不会影响软键盘的显示。 

    2.hideSoftInputFromWindow 

    hideSoftInputFromWindow会进入hideCurrentInputLocked()方法,在hideCurrentInputLocked()方法的开头有这样一段:

这里的判断用到了隐藏软键盘时使用的flags,以及mShowExplicitlyRequested和mShowForced的值,如果不满足条件就直接返回了。可以看到当flags为HIDE_IMPLICIT_ONLY时,如果mShowExplicitlyRequested和mShowForced任意一个为true,都会返回false。当flags为HIDE_NOT_ALWAYS时,如果mShowForced为true,也会返回false,当flags为0时,两个if条件都不满足。这就解释了显示软键盘时使用的flags影响的是后面隐藏软键盘是否成功,以及隐藏软键盘时使用0作为flags总是能够隐藏软键盘

结论

显示软键盘最可靠的方法如下:

view必须是VISIBLE的EditText,如果不是VISIBLE的,需要先将其设置为VISIBLE。 当前界面必须已经加载完成,不能直接在Activity的onCreate(),onResume(),onAttachedToWindow()中使用,可以在这些方法中通过postDelayed的方式来延迟执行showSoftInput()

隐藏软键盘最方便的方法如下:

view可以当前布局中已经存在的任何一个View,如果找不到可以用getWindow().getDecorView()

你可能感兴趣的:(Android显示和隐藏软键盘方法)