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)
        }
    }
}
相关推荐
robotx2 小时前
安卓线程相关
android
消失的旧时光-19433 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon4 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon4 小时前
VSYNC 信号完整流程2
android
dalancon4 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户69371750013845 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android6 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才6 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶7 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle
汪海游龙7 小时前
开源项目 Trending AI 招募 Google Play 内测人员(12 名)
android·github