背景介绍
在不沉浸状态栏时,当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.zzzimport 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.sleepclass 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 = 0var cnt = 0while (diff == 0 && cnt++ < 20) {sleep(50)if (f.dialog == null) continueval v = view?:return@Thread// https://github.com/mikepenz/MaterialDrawer/blob/aa9136fb4f5b3a80460fe5f47213985026d20c88/library/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.javaval 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 rval height = v.context.resources.displayMetrics.heightPixelsdiff = height - r.bottomLog.d("MySF", "diff: $diff")}Log.d("MySF", "diff out while: $diff")if (diff <= 0) return@ThreadLog.d("MySF", "f.dialog is ${f.dialog}")f.activity?.runOnUiThread {f.dialog?.window?.apply {val attr = attributesLog.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) {@Keepfun setY(y: Int) {val attr = window.attributesattr.y = yLog.d("MySF", "set y to $y")window.attributes = attr}}
}
Keyboard don’t resize the screen when android:windowTranslucentStatus=true ↩︎
Keyboard hiding EditText when android:windowTranslucentStatus=true ↩︎