安卓沉浸状态栏下 PreferenceFragment 弹出的输入对话框无法跟随键盘上移的解决办法

背景介绍

在不沉浸状态栏时,当PreferenceFragmentCompat中有EditTextPreference时,点击该条目呼出的输入对话框将跟随键盘上移,但一旦在styles.xml中设置

<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>

也就是沉浸状态栏或导航栏后,再呼出对话框时则无法上移。

历史回顾

这是一个2013年的陈年老bug1,从谷歌引入沉浸状态栏这个特性至今,该bug从未被修复,并且在stackoverflow也有不少帖子2,大致是通过手动上移控件的方式解决问题。

我的问题

这些帖子大多是针对自定义View上的EditText,所以自定义比较轻松,集大成者莫过于这个GitHub的回答,几乎可以适应一切布局,但在系统包装好的PreferenceFragmentCompat下,这个代码仅能检测到键盘呼出后的高度改变,却无法自动上移输入框。

解决办法

在综合上面的资料之后,结合PreferenceFragmentCompat的特性,我写出了最终解法,可能不是完美的办法,但是工作正常。

package xxx.yyy.zzz

import android.animation.ObjectAnimator
import android.graphics.Rect
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.Window
import androidx.annotation.Keep
import androidx.preference.EditTextPreference
import androidx.preference.EditTextPreferenceDialogFragmentCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import xxx.yyy.zzz.R
import java.lang.Thread.sleep

class SettingsFragment: PreferenceFragmentCompat() {
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.pref_setting, rootKey)
    }

    override fun onDisplayPreferenceDialog(preference: Preference) {
        if (preference is EditTextPreference) {
            Log.d("MySF", "preference is EditTextPreference")
            val f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.key)
            f.setTargetFragment(this, 0)
            f.show(parentFragmentManager, null)
            Thread {
                var diff = 0
                var cnt = 0
                while (diff == 0 && cnt++ < 20) {
                    sleep(50)
                    if (f.dialog == null) continue
                    val v = view?:return@Thread
                    // https://github.com/mikepenz/MaterialDrawer/blob/aa9136fb4f5b3a80460fe5f47213985026d20c88/library/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.java
                    val r = Rect()
                    //r will be populated with the coordinates of your view that area still visible.
                    v.getWindowVisibleDisplayFrame(r)
                    //get screen height and calculate the difference with the useable area from the r
                    val height = v.context.resources.displayMetrics.heightPixels
                    diff = height - r.bottom
                    Log.d("MySF", "diff: $diff")
                }
                Log.d("MySF", "diff out while: $diff")
                if (diff <= 0) return@Thread
                Log.d("MySF", "f.dialog is ${f.dialog}")
                f.activity?.runOnUiThread {
                    f.dialog?.window?.apply {
                        val attr = attributes
                        Log.d("MySF", "animate from ${attr.y} to ${attr.y-diff/2}")
                        ObjectAnimator.ofInt(WindowAttributeSetter(this), "y", attr.y, attr.y-diff/2).setDuration(233).start()
                    }
                }
            }.start()
            return
        }
        super.onDisplayPreferenceDialog(preference)
    }

    inner class WindowAttributeSetter(private val window: Window) {
        @Keep
        fun setY(y: Int) {
            val attr = window.attributes
            attr.y = y
            Log.d("MySF", "set y to $y")
            window.attributes = attr
        }
    }
}

  1. Keyboard don’t resize the screen when android:windowTranslucentStatus=true ↩︎

  2. Keyboard hiding EditText when android:windowTranslucentStatus=true ↩︎

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值