首先感谢以下博文与作者提供方案与思路
Android P控制虚拟键盘的显示和隐藏_show ime with external keyboard-CSDN博客
现象是,使用蓝牙键盘。在设置-系统-键盘-实体键盘-使用屏幕键盘开关进行开与关,发现无论开关状态谷歌屏幕键盘都是存在的。BUG认为开关关上以后应该不显示谷歌屏幕键盘。
/home/bcr/Documents/lcx/111.png
思路:
修改了InputMethodManager.showSoftInput(View, int)
方法,添加了以下逻辑:
- 检查是否连接了物理键盘
- 检查
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD
设置 - 如果连接了物理键盘且设置为不显示虚拟键盘,则直接返回false,阻止虚拟键盘显示
这个修改直接针对问题的根源:在显示虚拟键盘的入口处进行拦截。由于showSoftInput()
是所有应用调用虚拟键盘的共同路径,修改这个方法应该能够有效解决问题。
方案:
frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
首先添加一个检测是否连接了物理键盘方法
/**
* 检测是否连接了物理键盘
*
* @return 如果连接了物理键盘返回true,否则返回false
*/
private boolean isHardKeyboardConnected(Context context) {
// 方法1:通过Configuration检查
// Configuration config = context.getResources().getConfiguration();
// if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
// return true;
// }
// 方法2:通过InputManager检查(更可靠)
InputManager inputManager = context.getSystemService(InputManager.class);
if (inputManager != null) {
int[] deviceIds = inputManager.getInputDeviceIds();
for (int id : deviceIds) {
InputDevice device = inputManager.getInputDevice(id);
// 检查设备是否是物理键盘
if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
return true;
}
}
}
return false;
}
修改showSoftInput(View view, @ShowFlags int flags)
/**
* Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without
* a result receiver: explicitly request that the current input method's
* soft input area be shown to the user, if needed.
*
* @param view The currently focused view, which would like to receive soft keyboard input.
* Note that this view is only considered focused here if both it itself has
* {@link View#isFocused view focus}, and its containing window has
* {@link View#hasWindowFocus window focus}. Otherwise the call fails and
* returns {@code false}.
*/
public boolean showSoftInput(View view, @ShowFlags int flags) {
// 获取Context对象,通过view
Context context = view.getContext();
//linchengxian
// 检查是否连接了物理键盘
boolean hasHardKeyboard = isHardKeyboardConnected(context);
// 检查是否设置为在物理键盘连接时显示虚拟键盘
boolean showImeWithHardKeyboard = Settings.Secure.getInt(
context.getContentResolver(),
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
Log.d(TAG, "showSoftInput() hasHardKeyboard: " +hasHardKeyboard+
"showImeWithHardKeyboard: "+showImeWithHardKeyboard);
// 如果连接了物理键盘且设置为不显示虚拟键盘,则直接返回false
if (hasHardKeyboard && !showImeWithHardKeyboard) {
if (DEBUG) {
Log.d(TAG, "showSoftInput() blocked: physical keyboard connected and " +
"SHOW_IME_WITH_HARD_KEYBOARD is disabled");
}
return false;
}
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
return fallbackImm.showSoftInput(view, flags);
}
return showSoftInput(view, flags, null);
}
最后导包(用方法二Configuration不需要):
import android.view.InputDevice;
import android.content.res.Configuration;
import android.hardware.input.InputManager;
部分解释:
showSoftInput
方法有多个重载版本,形成了以下调用链:
showSoftInput(View, int)
- 最简单的公开接口showSoftInput(View, int, ResultReceiver)
- 添加结果接收器showSoftInput(View, int, ResultReceiver, int)
- 添加显示/隐藏原因showSoftInput(View, ImeTracker.Token, int, ResultReceiver, int)
- 最终实现版本
可能的原因:
InputMethodManager.showSoftInput()
方法没有正确检查SHOW_IME_WITH_HARD_KEYBOARD
设置- 或者没有正确检测物理键盘的连接状态
- 或者特定输入法(如谷歌拼音)可能有自己的逻辑覆盖了系统设置
后续测试 发现以上方案只针对点击输入框的情况,如果用外接键盘输入后,键盘还会显示出来。初步推测和谷歌输入法有关:
使用屏幕键盘、实体键盘相关设置有两处:
一个是BUG中的设置-系统下,一个是键盘Gboard设置中
(最新版本Gboard,如果不去手动升级最新,Gboard并没有显示屏幕键盘功能,只要你使用了物理键盘会自动收回屏幕键盘)
而逻辑会先遵循键盘Gboard设置中的设置,例如:设置下我将使用屏幕键盘关闭,使用屏幕键盘打开。则会使用屏幕键盘
观察了谷歌机Pixel7,发现可能Google也知道这块的矛盾,在Pixel7中已经将设置-系统下的使用屏幕键盘开关去除