10.1 软键盘呼出核心机制
软键盘显示条件
软键盘的显示需要同时满足以下条件:
- 焦点视图:当前有获得焦点的视图
- 输入能力:焦点视图是可输入的(如EditText)
- 输入模式:视图设置了正确的输入类型
- 系统策略:当前窗口允许软键盘显示
10.2 软键盘触发流程
标准触发流程源码解析
// EditText.java
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// 请求焦点并显示软键盘
requestFocus();
showSoftInput();
}
return super.onTouchEvent(event);
}
// View.java
public void requestFocus() {
// ... 标准焦点请求流程 ...
// 触发输入法显示
if (isInputMethodTarget()) {
InputMethodManager imm = getInputMethodManager();
if (imm != null) {
imm.viewClicked(this);
}
}
}
// InputMethodManager.java
public void viewClicked(View view) {
// 检查是否需要显示软键盘
if (view.isFocusable() && view.isFocusableInTouchMode() && view.isEnabled()) {
// 请求显示软键盘
showSoftInput(view, 0);
}
}
public boolean showSoftInput(View view, int flags) {
// 通过Binder调用系统服务
return mService.showSoftInput(
mClient, view.getWindowToken(), flags, null);
}
10.3 软键盘系统服务
InputMethodService架构
系统服务调用流程
// InputMethodManagerService.java
public boolean showSoftInput(IBinder token, int flags) {
// 1. 验证调用权限
enforceCallingPermission();
// 2. 获取焦点窗口
WindowState window = getFocusedWindow();
// 3. 检查窗口策略
if (!window.canShowSoftInput()) {
return false;
}
// 4. 获取当前输入法
InputMethodInfo imi = getCurrentInputMethod();
// 5. 创建输入法会话
InputMethodSession session = createSession(imi, token);
// 6. 显示软键盘
session.showSoftInput(flags);
return true;
}
10.4 焦点与软键盘的交互
焦点变更触发软键盘
// ViewRootImpl.java
void handleFocusChanged() {
View newFocus = getCurrentFocus();
// 焦点从输入视图转移到非输入视图
if (mLastFocusWasEditText && !(newFocus instanceof EditText)) {
hideSoftInput();
}
// 焦点转移到输入视图
else if (newFocus instanceof EditText) {
showSoftInputForView(newFocus);
}
mLastFocusWasEditText = (newFocus instanceof EditText);
}
private void showSoftInputForView(View view) {
InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
// 延迟显示以确保视图准备好
view.post(() -> imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT));
}
}
10.5 软键盘显示模式
窗口软输入模式
在AndroidManifest中配置Activity的软键盘行为:
<activity android:name=".MainActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
| 模式 | 描述 | 使用场景 | 焦点行为 |
|---|---|---|---|
| stateVisible | 进入时显示软键盘 | 登录页面 | 进入时自动聚焦到第一个输入视图 |
| stateHidden | 进入时隐藏软键盘 | 主页面 | 需要手动聚焦才会显示键盘 |
| stateAlwaysVisible | 总是显示软键盘 | 聊天界面 | |
| adjustResize | 调整窗口大小 | 大多数场景 | 焦点视图自动滚动到可视区域 |
| adjustPan | 平移窗口内容 | 全屏应用 | 焦点视图平移至不被键盘覆盖 |
| adjustNothing | 不做任何调整 | 特殊需求 | 焦点视图可能被键盘覆盖 |
模式组合效果
| 组合 | 行为 |
|---|---|
| stateVisible + adjustResize | 显示键盘时调整布局大小 |
| stateHidden + adjustPan | 进入时隐藏键盘,显示时平移内容 |
| stateAlwaysVisible + adjustNothing | 总是显示键盘且不调整布局 |
10.6 程序化控制软键盘
显示软键盘
public static void showSoftKeyboard(View view) {
if (view == null) return;
InputMethodManager imm = (InputMethodManager)
view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
// 确保视图获得焦点
view.requestFocus();
// 显示软键盘
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}
隐藏软键盘
public static void hideSoftKeyboard(Activity activity) {
View view = activity.getCurrentFocus();
if (view == null) {
view = new View(activity);
}
InputMethodManager imm = (InputMethodManager)
activity.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
切换软键盘状态
public static void toggleSoftKeyboard(Context context) {
InputMethodManager imm = (InputMethodManager)
context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
10.7 软键盘与布局调整
adjustResize模式实现原理
// ViewRootImpl.java
void reportSoftInputWindowGeometry() {
// 计算软键盘占据的区域
Rect visibleFrame = new Rect();
getVisibleFrame(visibleFrame);
// 通知视图系统可用区域变化
mAttachInfo.mVisibleInsets.set(
visibleFrame.left, visibleFrame.top,
visibleFrame.right, visibleFrame.bottom);
// 触发布局重新计算
requestLayout();
}
监听软键盘状态变化
// 在Activity中监听布局变化
public class KeyboardActivity extends AppCompatActivity {
private View mRootView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRootView = findViewById(R.id.root);
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(
this::checkKeyboardState);
}
private void checkKeyboardState() {
Rect visibleRect = new Rect();
mRootView.getWindowVisibleDisplayFrame(visibleRect);
int screenHeight = mRootView.getRootView().getHeight();
int keyboardHeight = screenHeight - visibleRect.bottom;
if (keyboardHeight > screenHeight * 0.15) {
// 软键盘显示
onKeyboardShown(keyboardHeight);
} else {
// 软键盘隐藏
onKeyboardHidden();
}
}
}
10.8 常见问题与解决方案
常见问题及解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 键盘不显示 | 视图未获得焦点 输入类型未设置 窗口模式配置错误 | 1. 确保view.requestFocus() 2. 设置android:inputType 3. 检查windowSoftInputMode |
| 键盘意外隐藏 | 焦点意外转移 窗口失去焦点 系统策略限制 | 1. 检查焦点监听器 2. 确保对话框关闭时恢复焦点 3. 使用SHOW_FORCED标志 |
| 键盘布局错误 | 输入类型不匹配 视图未实现InputConnection | 1. 正确设置inputType 2. 自定义视图实现onCreateInputConnection |
| 键盘遮挡内容 | 未使用adjustResize 焦点滚动逻辑错误 | 1. 设置adjustResize模式 2. 实现自定义滚动逻辑 |
调试工具
# 检查当前焦点视图
adb shell dumpsys activity top | grep "Focused"
# 检查输入法状态
adb shell dumpsys input_method
# 模拟键盘显示/隐藏
adb shell ime set com.android.inputmethod.latin/.LatinIME
adb shell input keyevent 66 # 显示键盘
adb shell input keyevent 4 # 隐藏键盘
问题1:软键盘不显示
原因分析:
- 视图未获得焦点
- 窗口策略禁止显示
- 输入类型配置错误
解决方案:
// 确保正确请求焦点
editText.postDelayed(() -> {
editText.requestFocus();
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
}, 100);
问题2:软键盘覆盖输入框
原因分析:
- 未正确设置
windowSoftInputMode - 全屏模式未正确处理
解决方案:
<!-- 在AndroidManifest中设置 -->
<activity android:name=".EditActivity"
android:windowSoftInputMode="adjustResize"/>
问题3:旋转后软键盘异常
原因分析:
- 旋转时未保存输入状态
- 焦点恢复顺序错误
解决方案:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 保存软键盘状态
outState.putBoolean("keyboard_visible", isKeyboardVisible());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 恢复软键盘状态
if (savedInstanceState.getBoolean("keyboard_visible")) {
editText.post(() -> showSoftKeyboard(editText));
}
}
10.9 高级技巧:自定义软键盘行为
控制软键盘动作按钮
<!-- 设置输入类型和动作 -->
<EditText
android:id="@+id/edittext"
android:inputType="text"
android:imeOptions="actionSearch"/>
editText.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
performSearch();
return true;
}
return false;
});
自定义软键盘布局
// 创建自定义软键盘视图
public class CustomKeyboardView extends KeyboardView {
public CustomKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
setKeyboard(new Keyboard(context, R.xml.custom_keyboard_layout));
}
}
// 在Activity中使用
public void showCustomKeyboard(View view) {
CustomKeyboardView keyboard = new CustomKeyboardView(this, null);
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.setInputMethod(view.getWindowToken(),
"com.example.app/.CustomInputMethodService");
}
10.10 调试工具与技术
ADB调试命令
# 显示软键盘
adb shell input keyevent KEYCODE_DPAD_CENTER
# 隐藏软键盘
adb shell input keyevent KEYCODE_BACK
# 切换软键盘
adb shell ime set com.android.inputmethod.latin/.LatinIME
# 列出所有输入法
adb shell ime list -a
# 强制显示软键盘
adb shell am broadcast -a show_soft_input
软键盘状态检查
// 检查软键盘是否显示
public boolean isKeyboardVisible() {
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.isAcceptingText();
}
// 获取当前输入法信息
public void logCurrentInputMethod() {
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
InputMethodInfo imi = imm.getCurrentInputMethodInfo();
if (imi != null) {
Log.d("InputMethod", "Current IME: " + imi.getPackageName());
}
}
本章小结
-
触发机制:
- 焦点视图 + 输入能力 + 系统策略
- 通过InputMethodManager系统服务控制
-
焦点交互:
- 焦点变更自动触发软键盘显示/隐藏
- EditText获得焦点时自动请求软键盘
-
显示模式:
stateVisible/stateHidden控制初始状态adjustResize/adjustPan控制布局调整
-
程序控制:
showSoftInput()显示软键盘hideSoftInputFromWindow()隐藏软键盘toggleSoftInput()切换状态
-
布局调整:
adjustResize模式下自动调整布局- 监听全局布局变化检测键盘状态
-
问题解决:
- 键盘不显示:确保视图获得焦点
- 键盘覆盖输入框:使用
adjustResize - 旋转后异常:保存/恢复键盘状态
-
高级技巧:
- 自定义软键盘动作
- 实现自定义软键盘
- 控制软键盘行为
-
调试工具:
- ADB命令模拟键盘操作
- 检查当前输入法
- 检测键盘可见状态
关键源码位置:
- InputMethodManager:
/frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java- InputMethodService:
/frameworks/base/core/java/android/inputmethodservice/InputMethodService.java- ViewRootImpl:
/frameworks/base/core/java/android/view/ViewRootImpl.java- WindowManagerService:
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
通过本章学习,您应该能够:
- 理解软键盘呼出的核心机制
- 程序化控制软键盘的显示和隐藏
- 处理软键盘与布局的交互
- 解决常见的软键盘相关问题
- 实现自定义软键盘行为
- 使用工具调试软键盘问题