如何让指定视图不被软键盘遮挡(转)

332 阅读2分钟

我们通常使用android:windowSoftInputMode属性来控制软键盘弹出之后移动界面,让输入框不被遮挡,但是有些场景下,键盘永远都会挡住一些我们使用频次比较高的控件,比如现在我们有个登录页面,大概的样子长这样

image.png

它的布局文件是这样

<RelativeLayout
    android:id="@+id/mainroot"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"
        android:src="@mipmap/ic_launcher_round" />

    <androidx.appcompat.widget.LinearLayoutCompat
        android:id="@+id/ll_view1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="120dp"
        android:gravity="center"
        android:orientation="vertical">

        <EditText
            android:id="@+id/main_edit"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:hint="请输入用户名"
            android:textColor="@color/black"
            android:textSize="15sp" />

        <EditText
            android:id="@+id/main_edit2"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_marginTop="30dp"
            android:hint="请输入密码"
            android:textColor="@color/black"
            android:textSize="15sp" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_marginHorizontal="10dp"
            android:layout_marginTop="20dp"
            android:text="登录" />

    </androidx.appcompat.widget.LinearLayoutCompat>

</RelativeLayout>
复制代码

在这样一个页面里面,由于输入框与登录按钮都比较靠页面下方,导致当输入完内容想要点击登录按钮时候,必须再一次关闭键盘才行,这样的操作在体验上就比较大打折扣了

aaaa.gif

现在希望可以键盘弹出之后,按钮也展示在键盘上面,这样就不用收起弹框以后才能点击按钮了,这样一来,windowSoftInputMode这一个属性已经不够用了,我们要想一下其他方案

  • 首先,需要让按钮也展示在键盘上方,那只能让布局整体上移把按钮露出来,在这里我们可以改变LayoutParam的bottomMargin参数来实现
  • 其次,需要知道键盘什么时候弹出,我们都知道android里面并没有提供任何监听事件来告诉我们键盘什么时候弹出,我们只能从其他角度入手,那就是监听根布局可视区域大小的变化

ViewTreeObserver

我们先获取视图树的观察者,使用addOnGlobalLayoutListener去监听全局视图的变化

bindingView.mainroot.viewTreeObserver.addOnGlobalLayoutListener {

}
复制代码

接下去就是要获取根视图的可视化区域了,如何来获取呢?View里面有这么一个方法,那就是getWindowVisibleDisplayFrame,我们看下源码注释就知道它是干什么的了

image.png

一大堆英文没必要都去看,只需要看最后一句就好了,大概意思就是获取能够展示给用户的可用区域,所以我们在监听器里面加上这个方法

bindingView.mainroot.viewTreeObserver.addOnGlobalLayoutListener {
    val rect = Rect()
    bindingView.mainroot.getWindowVisibleDisplayFrame(rect)
}
复制代码

当键盘弹出或者收起的时候,rect的高度就会跟着变化,我们就可以用这个作为条件来改变bottomMargin的值,现在我们增加一个变量oldDelta来保存前一个rect变化的高度值,用来做比较,完整的代码如下

var oldDelta = 0
val params:RelativeLayout.LayoutParams = bindingView.llView1.layoutParams as RelativeLayout.LayoutParams
val originBottom = params.bottomMargin
bindingView.mainroot.viewTreeObserver.addOnGlobalLayoutListener {
    val rect = Rect()
    bindingView.mainroot.getWindowVisibleDisplayFrame(rect)
    val deltaHeight = r.height()
    if (oldDelta != deltaHeight) {
        if (oldDelta != 0) {
            if (oldDelta > deltaHeight) {
                params.bottomMargin = oldDelta - deltaHeight
            } else if (oldDelta < deltaHeight) {
                params.bottomMargin = originBottom
            }
            bindingView.llView1.layoutParams = params
        }
        oldDelta = deltaHeight
    }
}
复制代码

最终效果如下

aaaa2.gif

弹出后页面有个抖动是因为本身有个页面平移的效果,然后再去计算layoutparam,如果不想抖动可以在布局外层套个scrollView,用smoothScrollTo把页面滑上去就可以了,有兴趣的可以业余时间试一下

参考:# 七道Android面试题,先来简单热个身