Android 监听软键盘的高度并解决其覆盖输入框的问题

1、前言

在某些项目中,我们常常需要自定义一个输入框,软键盘弹出时就把输入框顶上去,关闭时输入框再回到原位(比如下方的效果图,实际上各种 App 中的聊天界面和发布评论的界面大体都是这样)。在这个过程中,除了输入框以外的其他界面的元素不受影响,比如效果图中的背景图片不会上移也不会被压缩。但在实际使用中发现软键盘在弹出时常常把输入框盖住,导致输入框显示不完全。有什么方法可以解决呢?

效果图

2、思路分析

2.1 获取软键盘的高度

网上常见的思路是这样的:在输入框的下面放置一个View,当软键盘弹出时,获取软键盘高度,然后在代码中动态将该View的高度设置成跟软键盘的一样,这样输入框就被它顶上去了。从视觉上来看,就像是被软键盘顶上去一样。

这个思路的难点在于准确获取软键盘的动态高度。Android 系统没有提供直接获取软键盘高度的 api,好在我们可以曲线救国:软键盘的高度其实就是屏幕高度减去软键盘上方的可见区域(即没有被软键盘挡住的区域)高度,也就是:

软键盘高度 = 屏幕高度 - 可见区域高度

此外,还需要考虑状态栏和虚拟导航栏高度,所以我们可以得出以下的计算公式:

软键盘高度 = 屏幕高度 - 可见区域高度 - 顶部状态栏高度 - 底部导航栏高度

不过有两点需要注意:

  1. Activity 为全屏时是没有状态栏的,不必扣除高度;

  2. 横屏时虚拟状态栏是在侧边的,这时也不必扣除它的高度了。

最后我们的公式可以修正为:

软键盘高度 = 屏幕高度 - 可见区域高度 - 顶部状态栏高度(非全屏时) - 底部导航栏高度(竖屏时)

这个公式中的屏幕高度、状态栏高度和导航栏高度都可以通过 Android 的 api 获取,所以,现在问题的难点转换成了准确获取可见区域的动态高度

2.2 获取可见区域高度

准确获取可见区域的动态高度,何为准确,何为动态呢?要想准确,我们必须要准确获取可见区域的对象,要想动态,那必须监听可见区域的高度变化,也即是:

  1. 获取可见区域(对应准确);

  2. 监听可见区域的高度变化(对应动态)。

首先来看第一步,View类中为我们提供了一个方法getWindowVisibleDisplayFrame(),它可以获取某个View所在窗口(Window)的可见区域(注意:是窗口的可见区域,不是View的可见区域!)。它需要传入一个Rect对象,从Rect对象中,我们就可以获取到可见区域的信息,比如可见区域顶部距离父布局顶部的距离 top和可见区域底部部距离父布局顶部的距离bottom,两者一相减就是我们需要的可见区域高度了。

那么用哪一个View来获取可见区域呢?当前Activity或者Fragment上面的布局或者控件吗?答案是不行的。因为Activity(或Fragment)跟软键盘是位于同一个窗口的,也就是说,软键盘也在这个窗口的可见区域内,无论软键盘弹出还是关闭,可见区域的大小都不会变化!

既然如此,那么我们就需要另外一个窗口了。有没有办法创建一个不属于软键盘所在窗口的View呢?当然可以,DialogPopupWindow就可以办到。我们需要这个View一直存在,便于监听,所以PopupWindow无疑是最合适的。

第一步解决后,接下来就是监听可见区域的变化了这个比较简单,可以通过继承接口ViewTreeObserver.OnGlobalLayoutListener来,在onGlobalLayout()中监听来实现。

3、代码实践

思路已经捋清楚了,现在是代码时间。创建一个KeyboardStatusWatcher类,继承于PopupWindowViewTreeObserver.OnGlobalLayoutListener接口:

class KeyboardStatusWatcher(
    private val activity: FragmentActivity,
    private val lifecycleOwner: LifecycleOwner,
    private val listener: (isKeyboardShowed: Boolean, keyboardHeight: Int) -> Unit
) : PopupWindow(activity), ViewTreeObserver.OnGlobalLayoutListener {
   

    private val rootView by lazy {
    activity.window.decorView.rootView }

    private val TAG = "Keyboard-Tag"

    /**
     * 可见区域高度
     */
    private var visibleHeight = 0

    /**
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值