EditText中如何随心所欲的控制软键盘的展示与隐藏
前段时间在开发过程中,遇到了一个展示和隐藏软键盘的问题,具体case是这样的,在进入到页面A时,需要展示软键盘,通过edittext的操作然后在A中展示另一个view,出现这个view时候不能再展示软键盘了;还有种情况是,进入到A时需要立马显示那个view,显然这时候是不需要展示软键盘的。ok,需求交待清楚了,开始正常的尝试,想要进入A立即弹出软键盘,这时候很容易想到在manifest中去设置windowSoftInputMode,其中的内容有:
stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置
stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示
stateHidden:用户选择activity时,软键盘总是被隐藏
stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的
stateVisible:软键盘通常是可见的
stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态
adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示
adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间
adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分
所以很容易想到,使用stateAlwaysVisible这个属性,但是问题来了,我在显示view时候键盘还是在的,还有就是我在使用titleBar上的返回按钮返回时,这时候键盘仍然是在的,而且还无法满足进入A马上显示view时不展示软键盘的需求。所以我希望做的是,我自己可以去控制软键盘的展示和消失,然后网上查了一下,展示键盘的基本上都是这两句:
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(input, 0);
//或则通过使用inputMethodManager.showSoftInputFromInputMethod(input.getWindowToken(), 0);
当然这只是展示的,问题是这两句一直不生效,当然网上也有说解决方法,虽然有些方法是可以解决的,但是木有说出具体原因,而且个人认为解决的方式可能并不太恰当。
我们就来看看为什么这两句不生效,原因就在于input.getWindowToken()这个地方和是否拿到了input(EditText)的view的一些参数,先来看看getWindowToken()源码;
/**
* Retrieve a unique token identifying the window this view is attached to.
* @return Return the window's token for use in
* {@link WindowManager.LayoutParams#token WindowManager.LayoutParams.token}.
*/
public IBinder getWindowToken() {
return mAttachInfo != null ? mAttachInfo.mWindowToken : null;
}
可以看到假如说mAttachInfo = null的话,这个返回值肯定是空的,所以上面展示软键盘那句肯定是不生效的。
所以我采取的做法是:
input.post(new Runnable() {
@Override
public void run() {
input.requestFocus();
input.setFocusable(true);
if (input.getWindowVisibility() == View.VISIBLE) {
input.post(new Runnable() {
@Override
public void run() {
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(input, 0);
}
});
}
}
});
ok,可以看到我先是把这句if (input.getWindowVisibility() == View.VISIBLE)执行的放到了下一个消息队列中,然后又把
展示键盘的操作又放到了下一个下一个消息队列中。上面那句f (input.getWindowVisibility() == View.VISIBLE)要放倒下一个消息队列去执行的原因是需要等一个参数的位置话,具体还是看getWindowVisibility()的源码:
/**
* Returns the current visibility of the window this view is attached to
* (either {@link #GONE}, {@link #INVISIBLE}, or {@link #VISIBLE}).
*
* @return Returns the current visibility of the view's window.
*/
@Visibility
public int getWindowVisibility() {
return mAttachInfo != null ? mAttachInfo.mWindowVisibility : GONE;
}
可以看到我们是在等mAttachInfo 不为null,否则怎么也进不到这个if条件句中,还可以看到源码中:
/**
* A set of information given to a view when it is attached to its parent
* window.
*/
final static class AttachInfo {
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
}
...
final IBinder mWindowToken;
}
也就是说mWindowToken是AttachInfo中的成员变量。总的来说第一个post是为了等input的token,而后一个post也是必要的,但是我查了好久也不知道在等哪个变量操作,所以我也太清楚。但是敢保证的一点是这种写法是没有问题的,我看到网上还有一种写法是,用postDelay来延迟2s(具体多少毫秒我忘了)执行展示软键盘,但是也木有说原因,只说了1s(具体多少毫秒我忘了)不行,我个人认为那种写法并没有这种靠谱,因为根本找不到一个精确的delay时间,所以我用post放在下一个消息队列里执行。
OK,讲完这个软键盘显示了,下面讲一下软键盘隐藏,因为软键盘显示是在进入该页面时候,需要等待的一些初始化,但是在隐藏键盘时就可以不用考虑那些初始化了,因为基本上进行某些操作后才会进行隐藏键盘的需求。具体的写法是:
input.clearFocus();
if (input.getWindowVisibility() == View.VISIBLE) {
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(input.getWindowToken(), 0);
}
隐藏软键盘这部分是完全没有问题的。
OK,总结来说,如果你希望能够自由的去控制软键盘,可以使用我提供的这种做法:不要在manifest中去设置windowSoftInputMode了,然后使用上面提供展示软键盘的方法去展示键盘,需要隐藏掉软键盘是,就用相应的隐藏的方法。OK,就这些,其中有问题的地方,欢迎各位大神批评指正~