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)
        }
    }
}
相关推荐
阿巴斯甜21 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker21 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android