源码分析软键盘弹出失效问题
平常开发中,经常碰到 EditText
输入内容时需要调起软件盘。但是有时发现调用了之后没有生效,软键盘并没有弹出来。通常做法如下,延迟执行,虽然大概率提升了软键盘弹出的几率,但是偶尔还是会失效,没有从根本上解决问题的痛点。
editTextView.postDelayed(new Runnable() {
@Override
public void run() {
InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
manager.showSoftInput(editTextView, 0);
}
},500);
下面我们就从源码着手来分析一下问题所在。InputMethodManager
管理者输入相关逻辑,自然就包括软键盘的显示与隐藏,我们从 showSoftInput()
方法作为入口来看。
InputMethodManager.java
final IInputMethodManager mService;
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
......
try {
return mService.showSoftInput(mClient, flags, resultReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
......
}
这里会最终调用 InputMethodManagerService
的 showSoftInput()
方法。 InputMethodManagerService
为 IInputMethodManager.Stub
的一个实现类
InputMethodManagerService.java
@Override
public boolean showSoftInput(IInputMethodClient client, int flags,
ResultReceiver resultReceiver) {
......
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
//槟果~~~大致意思就是,这玩意在这里会检查一下当前 window 的焦点,
//如果没有获取焦点的话,直接返回 false 就失效了 - -!
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
return false;
}
......
return showCurrentInputLocked(flags, resultReceiver);
}
来,我们在看下这个 mIWindowManager
的 inputMethodClientHasFocus
WindowManagerService.java
@Override
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
synchronized (mWindowMap) {
// TODO: multi-display
//通过默认的显示设备的焦点进行判断,因为开始流行牛逼的分屏了。。哈哈
if (getDefaultDisplayContentLocked().inputMethodClientHasFocus(client)) {
return true;
}
// Okay, how about this... what is the current focus?
// It seems in some cases we may not have moved the IM
// target window, such as when it was in a pop-up window,
// so let's also look at the current focus. (An example:
// go to Gmail, start searching so the keyboard goes up,
// press home. Sometimes the IME won't go down.)
// Would be nice to fix this more correctly, but it's
// way at the end of a release, and this should be good enough.
// 这段话的大致意思就是 他们通过这个修复了一个系统的bug,
// 进入 邮件app ,弹出软键盘,立即安 home 键返回,
// 这个时候软键盘还在,不符合预期
if (mCurrentFocus != null && mCurrentFocus.mSession.mClient != null
&& mCurrentFocus.mSession.mClient.asBinder() == client.asBinder()) {
return true;
}
}
return false;
}
到这里我们就大致明白为啥软键盘有时候没有弹出来呢,就是当前 window
没有获取到焦点,即当前 window
是否已经完全在当前屏幕上显示出来。
原理我们已经了解了,具体怎么解决呢。其实,不管在 Activity
中,或者在 View
中都有一个 onAttachedToWindow()
方法。所以,在这个方法中调起软键盘就可以正常显示了。
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
manager.showSoftInput(editTextView, 0);
}