背景の紹介
ステータスバーが埋まっていない場合、PreferenceFragmentCompat
に EditTextPreference
がある場合、そのエントリをクリックすると呼び出される入力ダイアログ ボックスが上に移動します。キーボードを使用しますが、styles.xml
に設定すると
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
つまり、ステータス バーまたはナビゲーション バーを埋め込んだ後、ダイアログ ボックスを呼び出すときに上に移動することはできません。
歴史の要約
これは 2013 年からの古いバグです1。Google が没入型ステータス バー機能を導入して以来、このバグは解決されていません。修復、 stackoverflow2 には、コントロールを手動で上に移動することで問題を大まかに解決する多くの投稿があります。
私の質問
これらの投稿のほとんどはカスタム ビューの 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
}
}
}