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

背景介绍

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

<item name="android:windowTranslucentStatus">trueitem>
<item name="android:windowTranslucentNavigation">trueitem>

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

历史回顾

这是一个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 ↩︎

你可能感兴趣的:(安卓,kotlin,android,kotlin,bug,个人开发,安卓,ui,键盘)