博客搬迁到这里 http://blog.fdawei.club,欢迎访问,大家一起学习交流。
Android开发中,对键盘的控制是家常便饭。我们一般通过两种方式控制键盘的行为,一种是在Activity中配置相关属性,另一种是直接调用系统提供的方法。
AndroidManifest中配置
AndroidManifest文件的Activity中有一个名为android:windowSoftInputMode的属性,通过配置该属性可以实现对软键盘行为的一些控制。这个属性有有以下可配置的值
- stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置
- stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示
- stateHidden:用户选择activity时,软键盘总是被隐藏
- stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的
- stateVisible:软键盘通常是可见的
- stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态
- adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示
- adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间
- adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分
stateAlwaysVisible与stateVisible属性看上去是一样的效果,其实有小小的不同之处。举个例子,当我们设置为stateVisible属性,如果当前的界面键盘是显示的,当我们点击按钮跳转到下个界面的时候,软键盘会因为输入框失去焦点而隐藏起来,当我们再次回到当前界面的时候,键盘这个时候是隐藏的。但是如果我们设置为stateAlwaysVisible,我们跳转到下个界面,软键盘还是隐藏的,但是当我们再次回来的时候,软键盘是会显示出来的。所以,这个Always就解释了这个区别,不管什么情况到达当前界面(正常跳转或者是上一个界面被用户返回),软键盘都是显示状态。
stateAlwaysHidden和stateHidden不知道是不是也是一样的区别,没有验证。知道的同学还望赐教。
一般来说,在给它设置值的时候都是一个stateXXX + 一个adjustXXX,值之间用|分割开。用的比较多的还是"stateHidden|adjustResize"和"stateHidden|adjustPan"这两对组合。
"adjustResize"在使用时布局会被软键盘顶上去,体验非常不好
"adjustPan"在使用时获取焦点的控件下边的View将会被软键盘覆盖
不知道各位在开发中有没有遇到需要监听软键盘弹出或隐藏的需求,甚至是测量键盘占据的高度,其中一种实现方式就会用到这个属性。有兴趣的可以看这里 在Activity中监听软键盘的弹出与隐藏
顺便说一下,对于页面中包含EditText时,页面展现是,EditText会自动获取焦点,并触发软键盘的弹出,有时候我们不想让软键盘弹出,怎么办?有两种方式解决
方法一:将android:windowSoftInputMode设置为"stateHidden"或者“stateAlwaysHidden”
方法二:可以设置EditText外层View的focusable="true"、android:focusableInTouchMode="true",然后在onCreate的时候requsetFocus,主动获取焦点,这样就抢占掉了EditText的焦点,而又不会触发软键盘弹出
代码中控制
除了配置Activity的android:windowSoftInputMode属性,有时候也需要通过代码控制软键盘的弹出和隐藏,这样更加的灵活。
系统提供了InputMethodManager这个系统服务来供我们使用。首先获取这个系统服务,很简单,就像获取其他系统服务一样
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)
InputMethodManager提供了为数不多的几个方法,如下
显示软键盘
public boolean showSoftInput(View view, int flags)
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver)
public void showSoftInputFromInputMethod(IBinder token, int flags)
针对这几个显示软键盘的方法,说明如下
- showSoftInput第一个参数view必须是EditText,或者EditText的子类,如果是其他类型的View,如Button,TextView等,showSoftInput()方法不起作用。
- showSoftInput第一个参数view必须是可以获取焦点的(即view.isFocusable()返回true),如果不能获取焦点,则showSoftInput()方法不起作用。EditText默认是可获取焦点的,所以此条件一般都可以满足。如果不满足,可以通过view.setFocusable(true);将其设置为可获取焦点的View。
- showSoftInput第一个参数view当前必须已经获取到焦点(即view.isFocused()返回true),如果当前焦点不在该view上,则showSoftInput()方法不起作用。虽然EditText默认是可获取焦点的,但由于一个布局中可能会有多个控件可以获取焦点,焦点位置不一定会恰好在EditText上,所以此条件不一定满足。为了让showSoftInput()可以起作用,必须在之前showSoftInput()前先通过view.requestFocus();获取焦点。然后再执行showSoftInput()。
- showSoftInput第一个参数view必须是可见的,即view.getVisibility()等于View.VISIBLE,如果view是不可见的,无论view.getVisibility()是View.INVISIBLE还是View.GONE,showSoftInput()方法都不起作用。如果view是不可见的,可以先通过view.setVisibility(View.VISIBLE)将其设置为可见。
- 当前布局必须已经完成加载,如果还未绘制完成,则showSoftInput()方法不起作用。特别的,在Activity的onCreate()中执行showSoftInput()是不起作用的。如果要在布局文件加载后就显示软键盘,可以通过postDelayed的方式来延迟执行showSoftInput()。延迟时间不能太短,一般要在50ms以上。
- showSoftInputFromInputMethod和showSoftInput不同的是,第一个参数传入的不是一个View对象,而是一个View对象的windowToken(对一个view对象可以通过getWindowToken()的方法获取到其windowToken)。不过实际情况是,即使上述所有条件都满足,通过showSoftInput(view,flag)已经可以显示软键盘,但是通过showSoftInputFromInputMethod(view.getWindowToken(), flag)仍然无法显示软键盘。
- 参照Android官方文档,第二个参数提供一些额外的操作标记(additional operating flags),可以取0或者SHOW_IMPLICIT,0表示什么含义没有说明,SHOW_IMPLICIT表示本次显示软键盘的请求不是来自用户的直接请求,而是隐式的请求。且不说一会用数字,一会用常量名,光SHOW_IMPLICIT的说明恐怕除了这个接口的开发人员,没人能看懂这句话是什么意思。实际上这个参数还可以取第三个值SHOW_FORCED,直接在文档中被遗忘了。经过试验,这个参数的取值对软键盘的显示没有任何影响,无论取哪一个值软键盘都能够正常显示(即使随便输入一个整数,软键盘都可以显示)。实际上这个参数影响的并不是软键盘的显示,而是软键盘的隐藏,会在后文中讲到。
隐藏软键盘
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags)
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver)
public void hideSoftInputFromInputMethod(IBinder token, int flags)
- 第一个参数并不是指定一个View,而是一个View的windowToken。对一个view可以通过getWindowToken()的方法获取到其windowToken。
- hideSoftInputFromWindow按照官方文档,第一个参数中的windowToken应当是之前请求显示软键盘的View的windowToken,也就是执行showSoftInput()时第一个参数中的View的windowToken。但是实际情况是,用任意一个当前布局中的已经加载的View的windowToken都可以隐藏软键盘,哪怕这个View被设置为INVISIBLE或GONE。因此,如果不知道之前是谁请求显示的软键盘,可以随便传入一个当前布局中存在的View的windowToken。特别的,可以传入一个Activity的顶层View的windowToken,即getWindow().getDecorView().getWindowToken(),来隐藏当前Activity中显示的软键盘,而不用管之前调用showSoftInput()的究竟是哪个View。
- 参照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表示不能隐藏。
参数 | 0 | SHOW_IMPLICIT | SHOW_FORCED |
---|---|---|---|
0 | T | T | T |
HIDE_IMPLICIT_ONLY | F | T | F |
HIDE_NOT_ALWAYS | T | T | F |
可以看到,在隐藏软键盘时使用HIDE_IMPLICIT_ONLY标记,确实只有在显示软键盘时使用SHOW_IMPLICIT时才会隐藏。此外,当隐藏软键盘时使用0作为标记,无论showSoftInput()时使用的是哪个参数,都可以隐藏软键盘。
toggleSoftInput
在InputMethodManager类中还提供了一个toggleSoftInput的方法来在显示和隐藏软键盘之间切换。
public void toggleSoftInput(int showFlags, int hideFlags)
它同样有两个参数,第一个参数是显示软键盘时使用的标记,第二个参数是隐藏软键盘时使用的标记。
使用InputMethodManager的toggleSoftInput()方法来切换软键盘显示状态有如下注意事项。
- showFlags为显示软键盘时使用的标记,只有当前软键盘处于隐藏状态时才会使用。hideFlags是隐藏软键盘时使用的标记,只有当前软键盘处于显示状态时才会使用。
- showFlags和hideFlags取值范围,以及不同取值的影响和上文分析的一样。即showFlags和hideFlags都只影响软键盘的隐藏,不影响软键盘的显示。不同取值对软键盘隐藏的影响参见上文中的表格。
- 和showSoftInput()方法不同的是,使用toggleSoftInput()显示软键盘时,并不要求当前界面布局中有一个已经获取焦点的EditText,即使当前布局是完全空白的,一个View也没有(除了最外层的Layout),toggleSoftInput也能够显示软键盘。不过如果没有一个已经获取焦点的EditText,那么软键盘中的按键输入都是无效的。
- 显示软键盘时,要求当前布局必须已经加载完成,如果还未绘制完成,则toggleSoftInput()方法不起作用。这点和之前调用showSoftInput()显示软键盘时描述的第5点要求是一样的。特别的,在Activity的onCreate()中执行toggleSoftInput()必须通过postDelayed的方式来延迟执行。延迟时间一般要在50ms以上。
- 当隐藏软键盘时,不需要知道之前触发软键盘显示的View是哪一个或获取当前布局中任何一个View的windowToken,只要hideFlags能够隐藏就可以。由于hideFlags为0时总是能够隐藏的,因此,使用toggleSoftInput(0, 0)应当是最方便的无条件隐藏软键盘的方法,前提是知道当前软键盘确实是处于显示状态。不过遗憾的是Android没有任何API可以直接获取到软键盘的状态是显示还是隐藏的。
这里部分引用这篇博客,感谢原作者