一、简述
云游戏输入法分两种情况,以云化原神为例,分为 云端输入法
和 本地输入法
,运行效果如下:
云端输入法 | 本地输入法 |
---|---|
云端输入法
就是运行在云端设备上的输入法,对于不同客户端来说(Android、iPhone),运行效果一致。
本地输入法
则是运行在用户侧设备上的输入法,对于不同客户端来说(Android、iPhone),运行效果不一致。
从开发角度来说,云端输入法
是最省心的,无须关心输入法事件,只管同步触屏事件即可。但是,从功能性角度来说,本地输入法
可以有更好的体验,能为非触屏设备提供云游戏文字交互能力,例如 TV 端(遥控器操控)。
本地输入法
需要前后端配合开发,后端需提供输入法唤起事件回调,读取和注入文字的能力,这个以后有机会再做详细介绍。本篇主要讲述 前端(Android 客户端)实现,其实仔细分析一下就能看出,这个 本地输入法
功能,无非是在输入法之上显示了一个 悬浮输入框
,并且与输入法同步显隐,所以,本篇的核心内容,就是如何来实现这么一个 悬浮输入框
。
二、方案
在说明具体方案之前,需要先了解下 Android 窗口对于输入法的主要 adjust
策略:
分类 | 说明 |
---|---|
adjustResize | 始终调整 activity 主窗口的尺寸,以为屏幕上的软键盘腾出空间。 |
adjustPan | 不通过调整 activity 主窗口的尺寸为软键盘腾出空间。相反,窗口的内容会自动平移,使键盘永远无法遮盖当前焦点,以便用户始终能看到自己输入的内容。这通常不如调整窗口尺寸可取,因为用户可能需关闭软键盘才能进入被遮盖的窗口部分,并与之进行交互。 |
- 图片出处:https://code.luasoftware.com/tutorials/android/move-layout-when-keyboard-shown
adjust
官方说明:https://developer.android.com/guide/topics/manifest/activity-element?hl=zh-cn
悬浮输入框
并不是一个新课题,网上有其他 Android 开发者分享过自己实现 悬浮输入框
的方案,他们的做法,大致能分为以下几种:
方案 | 原理 |
---|---|
基于 View 实现 | 通过监听输入法高度,调整页面底部 View 的 y 值偏移量 |
基于 Activity 实现 基于 Dialog 实现 |
利用 adjustResize 特性,将处于窗口底部的输入框,自动被输入法顶上去 |
- https://blog.csdn.net/chzphoenix/article/details/122863199
- https://segmentfault.com/a/1190000011541743
- https://cloud.tencent.com/developer/article/1153415
那么这几种方案会有什么问题呢?
方案 | 问题 |
---|---|
基于 View 实现 | Android SDK 没有提供监听与获取输入法高度的 API,早期主流做法是使用 ViewTreeObserver 的 OnGlobalLayoutListener ,监听布局高度变化,进而计算出输入法高度,但是设备兼容性可能存在问题,在不同设备上拿到的输入法高度不一定准确,适配成本较高。 |
基于 Activity 实现 | 需要编写一个新的透明 Activity,在清单文件中注册 Activity 和指定 windowSoftInputMode 为 adjustResize ,这个方案利用了 adjustResize 特性,非常讨巧,无须计算输入法高度,适配成本较低,但是,实现上比较复杂笨重,特别是输入框监听事件需要在两个 Activity 之间传递时,无法很优雅的实现。 |
而 基于 Dialog 实现
方案,可以很好的规避掉上述方案存在的问题。因为 Dialog
和 Activity
一样,拥有自己的 Window
,而 windowSoftInputMode
实际上是作用于 Window
的,即 Dialog
可以设置自己的 windowSoftInputMode
:
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
另外,Dialog
可以很轻易地把输入框事件回调给外部(Activity、Fragment 等)。
注:
Dialog
无须在清单文件中注册,在动态更新上也有一定的优势。
三、实现
经过上面的分析,我最终采用的是 基于 Dialog 实现
方案,来实现 悬浮输入框
。要注意的地方大致有这几点:
- Dialog 样式与布局
- 监听输入法显隐
- 兼容横竖屏切换
1、Dialog 样式与布局
系统默认的 Dialog 是有样式的,需要对其进行定制,修改为透明的 Dialog,另外还要修改窗口的 windowSoftInputMode
,具体代码如下:
/**
* 基于 Dialog 实现的悬浮输入框
*
* @author LQR
* @since 2024/7/6
*/
public class FloatInputDialog extends AbsFloatInputDialog {
protected View rootView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = initView();
initWindow();
}
protected View initView() {
// 自定义布局
View rootView = View.inflate(