Android - 云游戏本地悬浮输入框实现

发布于:2024-07-09 ⋅ 阅读:(62) ⋅ 点赞:(0)

一、简述

云游戏输入法分两种情况,以云化原神为例,分为 云端输入法本地输入法,运行效果如下:

云端输入法 本地输入法

云端输入法 就是运行在云端设备上的输入法,对于不同客户端来说(Android、iPhone),运行效果一致。
本地输入法 则是运行在用户侧设备上的输入法,对于不同客户端来说(Android、iPhone),运行效果不一致。

从开发角度来说,云端输入法 是最省心的,无须关心输入法事件,只管同步触屏事件即可。但是,从功能性角度来说,本地输入法 可以有更好的体验,能为非触屏设备提供云游戏文字交互能力,例如 TV 端(遥控器操控)。

本地输入法 需要前后端配合开发,后端需提供输入法唤起事件回调,读取和注入文字的能力,这个以后有机会再做详细介绍。本篇主要讲述 前端(Android 客户端)实现,其实仔细分析一下就能看出,这个 本地输入法 功能,无非是在输入法之上显示了一个 悬浮输入框,并且与输入法同步显隐,所以,本篇的核心内容,就是如何来实现这么一个 悬浮输入框

二、方案

在说明具体方案之前,需要先了解下 Android 窗口对于输入法的主要 adjust 策略:

分类 说明
adjustResize 始终调整 activity 主窗口的尺寸,以为屏幕上的软键盘腾出空间。
adjustPan 不通过调整 activity 主窗口的尺寸为软键盘腾出空间。相反,窗口的内容会自动平移,使键盘永远无法遮盖当前焦点,以便用户始终能看到自己输入的内容。这通常不如调整窗口尺寸可取,因为用户可能需关闭软键盘才能进入被遮盖的窗口部分,并与之进行交互。

悬浮输入框 并不是一个新课题,网上有其他 Android 开发者分享过自己实现 悬浮输入框 的方案,他们的做法,大致能分为以下几种:

方案 原理
基于 View 实现 通过监听输入法高度,调整页面底部 View 的 y 值偏移量
基于 Activity 实现
基于 Dialog 实现
利用 adjustResize 特性,将处于窗口底部的输入框,自动被输入法顶上去

那么这几种方案会有什么问题呢?

方案 问题
基于 View 实现 Android SDK 没有提供监听与获取输入法高度的 API,早期主流做法是使用 ViewTreeObserverOnGlobalLayoutListener,监听布局高度变化,进而计算出输入法高度,但是设备兼容性可能存在问题,在不同设备上拿到的输入法高度不一定准确,适配成本较高。
基于 Activity 实现 需要编写一个新的透明 Activity,在清单文件中注册 Activity 和指定 windowSoftInputModeadjustResize,这个方案利用了 adjustResize 特性,非常讨巧,无须计算输入法高度,适配成本较低,但是,实现上比较复杂笨重,特别是输入框监听事件需要在两个 Activity 之间传递时,无法很优雅的实现。

基于 Dialog 实现 方案,可以很好的规避掉上述方案存在的问题。因为 DialogActivity 一样,拥有自己的 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(

网站公告

今日签到

点亮在社区的每一天
去签到