Android全屏模式下,即使设置windowSoftInputMode为adjustResize,键盘未推移Activity导致无法生效

问题

当输入框处于最底部时

当我们点击输入框,唤起软键盘,会挡住我们的输入框内容 如图

解决方案 1 设置 android:windowSoftInputMode="adjustResize"

这种方式会把页面整体上推 可以看到头部标题整体向上平移了 虽然也能解决问题 但是ui上不友好

解决方案 2 android:windowSoftInputMode="adjustResize" + ScrollView

当时要满足两种情况下才会生效

  1. 需要界面本身可调整尺寸
  2. 页面不是全屏时
我们项目中 满足条件1 我们在布局外层包裹NestedScrollView 但是我们是全屏的布局 这就导致第二个条件不满足 所以得寻求解决方案 这样就衍生出了 AndroidBug5497Workaround

既在非全屏模式(即状态栏不透明)下,将activitywindowSoftInputMode的属性设置为:adjustResize。同时在ViewonSizeChanged(int w, int h, int oldw, int oldh)里可以得到变化后的尺寸,然后根据前后变化的结果来计算屏幕需要移动的距离。

但是在全屏模式下,即使将activitywindowSoftInputMode的属性设置为:adjustResize。 在键盘显示时它未将Activity的Screen向上推动,所以你Activityview的根树的尺寸是没有变化的。 在这种情况下,你也就无法得知键盘的尺寸,对根view的作相应的推移。 全屏下的键盘无法Resize的问题从2.1就已经存在了,直到现在google还未给予解决。

终极解决方案 封装好了 上面有详细的备注

  1. 找到Activity的最外层布局控件,我们知道所有的Activity都是DecorView,它就是一个FrameLayout控件,该控件id是系统写死叫R.id.content,就是我们setContentView时,把相应的View放在此FrameLayout控件里 2.给我们的Activity的xml布局View设置一个Listener监听 View.getViewTreeObserver()可以获取一个ViewTreeObserver对象------它是一个观察者,用以监听当前View树所发生的变化。这里所注册的addOnGlobalLayoutListener,就是会在当前的View树的全局布局(GlobalLayout)发生变化、或者其中的View可视状态有变化时,进行通知回调。『软键盘弹出/隐 』都能监听到

  2. 获取当前界面可用高度

  3. 重设高度, 我们计算出的可用高度,是目前在视觉效果上能看到的界面高度。但当前界面的实际高度是比可用高度要多出一个软键盘的距离的。 注意:如果既使用了沉浸式状态栏,又加了fitSystetemWindow=true属性,就需要在AndroidMainfest.xml注册Activity的地方添加上以下属性。因为你两种都用,系统不知道用哪种了。fitSystetemWindow已经有resize屏幕的作用。

4.所以content.getChildAt(0)获取到的mChildOfContent,也就是我们用setContentView放进去的View。

kotlin 复制代码
class AndroidBug5497Workaround private constructor(activity: Activity, needStatusBars: Boolean) {
    private var mChildOfContent: View? = null
    private var usableHeightPrevious = 0
    private var frameLayoutParams: FrameLayout.LayoutParams? = null
    private var contentHeight = 0
    private var isfirst = true
    private var statusBarHeight = 0
    private var needStatusBar = true

    init {
        try {
            //获取状态栏的高度
            val resourceId = activity.resources.getIdentifier("status_bar_height", "dimen", "android")
            statusBarHeight = activity.resources.getDimensionPixelSize(resourceId)
            val content = activity.findViewById<FrameLayout>(R.id.content)
            mChildOfContent = content.getChildAt(0)
            needStatusBar = needStatusBars
            //界面出现变动都会调用这个监听事件
            mChildOfContent?.let {
                it.getViewTreeObserver().addOnGlobalLayoutListener {
                    if (isfirst) {
                        contentHeight = it.height //兼容华为等机型
                        isfirst = false
                    }
                    if (activity.isDestroyed || activity.isFinishing) return@addOnGlobalLayoutListener
                    possiblyResizeChildOfContent()
                }
                frameLayoutParams = it.layoutParams as FrameLayout.LayoutParams
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    //重新调整跟布局的高度
    private fun possiblyResizeChildOfContent() {
        val usableHeightNow = computeUsableHeight()

        //当前可见高度和上一次可见高度不一致 布局变动
        if (usableHeightNow != usableHeightPrevious) {
            //int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容华为等机型
            val usableHeightSansKeyboard = (mChildOfContent?.rootView?.height) ?: 0
            val heightDifference = usableHeightSansKeyboard - usableHeightNow
            var height = contentHeight
            height = if (heightDifference > usableHeightSansKeyboard / 4) {
                // keyboard probably just became visible
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    //frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                    usableHeightSansKeyboard - heightDifference + if (!needStatusBar) 0 else statusBarHeight
                } else {
                    usableHeightSansKeyboard - heightDifference
                }
            } else {
                contentHeight
            }
            YzLog.e("possiblyResizeChildOfContent========height=$height usableHeightNow=$usableHeightNow usableHeightSansKeyboard=$usableHeightSansKeyboard")

//            if (height>1080){
            frameLayoutParams?.height = height
            mChildOfContent?.requestLayout()
            //            }
            usableHeightPrevious = usableHeightNow
        }
    }

    /**
     * 计算mChildOfContent可见高度
     */
    private fun computeUsableHeight(): Int {
        val r = Rect()
        mChildOfContent!!.getWindowVisibleDisplayFrame(r)
        return r.bottom - r.top
    }

    companion object {
        fun assistActivity(activity: Activity?, needStatusBar: Boolean) {
            if (activity == null || activity.isDestroyed || activity.isFinishing) return
            AndroidBug5497Workaround(activity, needStatusBar)
        }

        fun assistActivity(activity: Activity?) {
            if (activity == null || activity.isDestroyed || activity.isFinishing) return
            AndroidBug5497Workaround(activity, true)
        }
    }
}
相关推荐
Blue.ztl2 小时前
菜鸟之路day31一一MySQL之多表设计
android·数据库·mysql
练习本5 小时前
Android系统架构模式分析
android·java·架构·系统架构
每次的天空10 小时前
Kotlin 内联函数深度解析:从源码到实践优化
android·开发语言·kotlin
练习本10 小时前
Android MVC架构的现代化改造:构建清晰单向数据流
android·架构·mvc
早上好啊! 树哥11 小时前
android studio开发:设置屏幕朝向为竖屏,强制应用的包体始终以竖屏(纵向)展示
android·ide·android studio
YY_pdd11 小时前
使用go开发安卓程序
android·golang
Android 小码峰啊13 小时前
Android Compose 框架物理动画之捕捉动画深入剖析(29)
android·spring
bubiyoushang88813 小时前
深入探索Laravel框架中的Blade模板引擎
android·android studio·laravel
cyy29813 小时前
android 记录应用内存
android·linux·运维
CYRUS STUDIO14 小时前
adb 实用命令汇总
android·adb·命令模式·工具