【免费下载链接】Keyboard
Easy keyboard for inserting all kinds of texts, special characters and numbers.
Fossify Keyboard 是一款完全离线、注重隐私的开源 Android 输入法,支持多种语言布局、内置剪贴板、表情面板与深色主题。它最核心的实现就是一个继承自 Android 系统类InputMethodService的服务——本文将带你从源码层面拆解Fossify Keyboard 的 InputMethodService 输入服务生命周期,看懂一个自定义键盘从注册、启动到把按键变成文字的完整链路。
一、InputMethodService:键盘应用的心脏
在 Android 里,所有第三方输入法都必须实现一个InputMethodService。系统(输入法管理器 IMS)会负责启动、绑定和切换它,而键盘 UI 与逻辑全部由开发者自己写。
Fossify Keyboard 的核心就集中在一个文件里:
- 输入服务入口:SimpleKeyboardIME.kt
class SimpleKeyboardIME : InputMethodService(), OnKeyboardActionListener { ... }它同时实现了OnKeyboardActionListener接口(见 OnKeyboardActionListener.kt),这样键盘视图在捕捉到触摸后,就能通过onPress、onKey、onActionUp等回调把事件交还给服务本身处理。
简单记忆:Service 管"状态与文字",View 管"显示与触摸",两者靠监听器接口互相回调。
二、服务注册:两步声明缺一不可
要让系统识别出这是一个输入法,Fossify Keyboard 做了两件标准动作。
1. 在清单中声明<service>
在 AndroidManifest.xml 中,服务被声明并绑定了一个特殊的元数据:
<service android:name=".services.SimpleKeyboardIME" android:exported="true" android:permission="android.permission.BIND_INPUT_METHOD"> <meta-data android:name="android.view.im" android:resource="@xml/method" /> <intent-filter> <action android:name="android.view.InputMethod" /> </intent-filter> </service>其中BIND_INPUT_METHOD权限和android.view.InputMethod动作告诉系统"这是一个输入方法",而meta-data指向的描述文件才是真正的"身份证"。
2. 编写输入法描述文件method.xml
method.xml 定义了输入法的图标、设置页与子类型:
<input-method android:icon="@mipmap/ic_launcher" android:settingsActivity="org.fossify.keyboard.activities.SettingsActivity" android:supportsInlineSuggestions="true"> <subtype android:imeSubtypeMode="Keyboard" /> </input-method>settingsActivity决定了用户点"输入法设置"时跳转到哪里(对应 SettingsActivity.kt)。
三、生命周期:从创建到按键的完整链路
InputMethodService提供了一系列回调,Fossify Keyboard 按需重写了其中关键的几个,它们共同构成了键盘的"一生":
| 回调方法 | 触发时机 | 主要职责 |
|---|---|---|
onInitializeInterface | 键盘框架初始化后 | 注册设置变化监听,实现"改设置即时生效" |
onCreateInputView | 需要显示键盘 UI 时 | 构建并返回键盘视图树 |
onStartInput | 聚焦到某个输入框时 | 读取输入类型、构建对应键盘 |
onStartInputView | 键盘视图显示后 | 应用背景色、适配刘海屏 |
onKey | 用户按下按键时 | 把按键码转换为文字并写入输入框 |
1. 构建键盘视图(onCreateInputView)
override fun onCreateInputView(): View { binding = KeyboardViewKeyboardBinding.inflate(layoutInflater) keyboardView = binding.keyboardView.apply { setKeyboard(keyboard!!) setEditorInfo(currentInputEditorInfo) setupEdgeToEdge() mOnKeyboardActionListener = this@SimpleKeyboardIME } return binding.root }它把布局 keyboard_view_keyboard.xml 膨胀成视图树。这个布局里包含工具栏、表情面板、剪贴板面板,以及真正负责按键显示的自定义视图MyKeyboardView(见 MyKeyboardView.kt)。注意最后把mOnKeyboardActionListener指向了自己——这就是"视图回调服务"的接线点。
2. 感知输入框(onStartInput)
每当用户聚焦到某个输入框,系统都会回调它。Fossify Keyboard 在这里判断输入框类型,动态切换键盘布局:
override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) { inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS enterKeyType = attribute.imeOptions and (IME_MASK_ACTION or IME_FLAG_NO_ENTER_ACTION) keyboard = createNewKeyboard() keyboardView?.setKeyboard(keyboard!!) ... }createNewKeyboard()会根据输入类型选择不同的布局资源:数字框用keys_numbers、电话框用keys_phone、日期框用符号键盘,其余默认用字母键盘。这就是为什么在数字输入框里,键盘会自动变成数字布局。
四、从按键到文字:触摸、回调与 commitText
1. 视图负责"点中了哪个键"
MyKeyboardView在onTouchEvent中把触摸坐标映射到具体按键,找到后调用监听器。核心逻辑非常简洁:
private fun getPressedKeyIndex(x: Int, y: Int): Int { return mKeys.indexOfFirst { it.isInside(x, y) } } private fun detectAndSendKey(index: Int, x: Int, y: Int, eventTime: Long) { if (index != NOT_A_KEY && index in mKeys.indices) { mOnKeyboardActionListener!!.onKey(mKeys[index].code) } }isInside(x, y)是MyKeyboard.Key里的几何判定(见 MyKeyboard.kt),它判断坐标是否落在按键矩形内。命中后就把按键码code通过onKey传回给服务。
2. 服务负责"把键变成字"
服务的onKey是真正的"输入引擎"。它拿到currentInputConnection(通往输入框的桥梁),再根据按键码决定写文字、删字符还是执行回车:
override fun onKey(code: Int) { val inputConnection = currentInputConnection when (code) { MyKeyboard.KEYCODE_DELETE -> { inputConnection.deleteSurroundingText(count, 0) // 退格 } MyKeyboard.KEYCODE_ENTER -> { inputConnection.performEditorAction(imeOptionsActionId) // 回车/搜索/发送 } else -> { inputConnection.commitText(codeChar.toString(), 1) // 提交字符 updateShiftKeyState() } } }其中commitText是关键 API:它把字符串插入到光标处,是键盘与输入框之间最核心的通信方式。退格键则用deleteSurroundingText删除,回车键用performEditorAction触发"搜索/发送"等动作。
3. 键盘数据来自 XML
MyKeyboard在构造时会解析一份布局 XML(如 keys_letters_english_qwerty.xml),把<Row>、<Key>标签逐行逐键读进内存,算出每个键的坐标、宽高与按键码。这也意味着:想新增一种语言或键盘布局,往往只需再写一份 XML,这也是 Fossify Keyboard 支持几十种语言的秘诀。
五、动态响应:让设置"改完即生效"
键盘是一个长期存活的组件,用户随时可能去改主题、换语言。Fossify Keyboard 在onInitializeInterface里注册了设置监听,一旦相关键值变化就立即刷新视图:
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { if (key in arrayOf(SHOW_KEY_BORDERS, KEYBOARD_LANGUAGE, HEIGHT_PERCENTAGE, ...)) { keyboardView?.setupKeyboard() updateBackgroundColors() } }配合onUpdateSelection(光标/选区变化时刷新 Shift 状态),键盘始终能跟上输入框的最新状态。这套"监听 + 即时重建"的思路,是自定义InputMethodService里很值得借鉴的工程实践。
小结
回顾 Fossify Keyboard 对InputMethodService的使用,可以总结出一条清晰的主线:
- 注册:
AndroidManifest.xml+method.xml向系统声明"我是一名输入法" - 创建:
onCreateInputView搭好键盘视图,并把监听器指向自身 - 启动:
onStartInput读取输入框类型,构建匹配的语言/数字键盘 - 输入:
MyKeyboardView命中按键 →onKey回调 →commitText写入文字 - 响应:设置监听 + 选区更新,让键盘始终保持最新状态
理解这条生命周期链路,你就掌握了 Android 自定义键盘最核心的骨架,无论是移植功能还是开发自己的输入法,都能从 Fossify Keyboard 这份清晰、克制的源码里获得大量参考。
【免费下载链接】Keyboard
Easy keyboard for inserting all kinds of texts, special characters and numbers.
相关推荐
prompt-ops前端界面详解:轻松掌握可视化优化工具
prompt ops前端界面详解:轻松掌握可视化优化工具 prompt ops是一款开源的LLM提示词优化工具,通过直观的前端界面帮助用户轻松实现提示词的可视化
Rust实现原理全揭秘:Pitchfork Supervisor、IPC与进程生命周期源码剖析
Rust实现原理全揭秘:Pitchfork Supervisor、IPC与进程生命周期源码剖析 Pitchfork 是一个用 Rust 编写的本地守护进程管理工
Fossify Keyboard 40+ 语言键盘布局实测:QWERTY、AZERTY 到 Dvorak 全解析
Fossify Keyboard 40+ 语言键盘布局实测:QWERTY、AZERTY 到 Dvorak 全解析 Fossify Keyboard 是一款完全开
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考