Background introduction
When the status bar is not immersed, when there is in PreferenceFragmentCompat
, the input dialog box called out by clicking the entry will move up along with the keyboard, but Once set inEditTextPreference
styles.xml
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
That is, after immersing the status bar or navigation bar, you cannot move up when calling out the dialog box.
Recap history
This is an old bug from 20131. Since Google introduced the immersive status bar feature, this bug has never been resolved. Repair, and there are many posts on stackoverflow2, which roughly solve the problem by manually moving the control up.
my question
Most of these posts are for the EditText on the custom View, so customization is relatively easy, and the best one is this GitHub answer, It can adapt to almost any layout, but under system packaging, this code can only detect the height change after the keyboard is called out, but cannot automatically move the input box up. PreferenceFragmentCompat
Solution
After synthesizing the above information and combining the characteristics of PreferenceFragmentCompat
, I wrote the final solution. It may not be a perfect solution, but it works fine.
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
}
}
}