これは非常に古い投稿ですが、私のプロジェクトではごく最近、これにも苦労しました。私はこのケースを完全に解決する解決策を思いついたので、誰かが私が経験した苦労を避けるのを助けるために共有したいと思いました。
アプリケーションがフルスクリーンであったため、windowSoftInputModeを「AdjustResize」に設定しても機能しませんでした(これは、ここで説明したAndroidのバグが原因です)。それが機能したとしても、このオプションはあまり良いUIではないと思います。そのため、「パンの調整」オプションを「パンの追加」で機能させることも望んでいましたが、残念ながらAndroidにはこのマージンを調整するためのAPIが用意されていません。また、editTextに「paddingBottom」を追加すると、画面のUIのバランスが崩れます。
多くの調査の結果、次の手順に従ってこれを解決することができました。(私はこの非常に役立つミディアムポストを使用しました)
1-AndroidManifestからwindowSoftInputMode="adjustUnspecificed"を作成します。
2-拡張関数として次のメソッドを追加します。
fun Activity.getRootView(): View {
return findViewById<View>(android.R.id.content)
}
fun Context.convertDpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
this.resources.displayMetrics
)
}
fun Activity.isKeyboardOpen(): Boolean {
val visibleBounds = Rect()
this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
val heightDiff = getRootView().height - visibleBounds.height()
val marginOfError = Math.round(this.convertDpToPx(50F))
return heightDiff > marginOfError
}
3-キーボードがいつ表示されるかを検出するためのグローバルレイアウトリスナーを以下のように作成します。
val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
// Keep a reference to the last state of the keyboard
private var lastState: Boolean = isKeyboardOpen() ?: false
/**
* Something in the layout has changed
* so check if the keyboard is open or closed
* and if the keyboard state has changed
* save the new state and invoke the callback
*/
override fun onGlobalLayout() {
val isOpen = isKeyboardOpen() ?: false
if (isOpen == lastState) {
return
} else {
onKeyboardStateChanged(isOpen)
lastState = isOpen
}
}
}
4-onKeyboardStateChangedメソッドでは、メインのアクティビティレイアウトをscreenHeight / 4で上にスクロールします。これは通常は十分ですが、適切と思われるスクロール量を試して、スクロールしたいレイアウトにレイアウトを変更できます。また、トランジションアニメーションを使用して、トランジションをスムーズにします。これは、機能には必要ありません。
private fun onKeyboardStateChanged(open: Boolean) {
runOnUiThread {
if (open) {
val screenHeight = resources.displayMetrics.heightPixels
TransitionManager.beginDelayedTransition(main_activity_layout as ViewGroup)
main_activity_layout.scrollTo(0, screenHeight / 4)
} else {
TransitionManager.beginDelayedTransition(main_activity_layout as ViewGroup)
main_activity_layout.scrollTo(0, 0)
}
}
}
5- onResumeでリッスンするビューを追加し、次のようにonPauseでリスナーを削除します。
override fun onResume() {
super.onResume()
val view = getRootView()
view.viewTreeObserver?.addOnGlobalLayoutListener(listener)
}
override fun onPause() {
super.onPause()
val view = getRootView()
view.viewTreeObserver?.removeOnGlobalLayoutListener(listener)
}
windowSoftInputModeを"adjustNothing"に設定した場合、これは機能しません。リスナーロジックが失敗するためです。
また、「adjustPan」と一緒に使用すると、このロジックが現在のスクロールポイントからパンを調整するため、意図したとおりに機能しません。たとえば、同じビューに2つの編集テキストがあり、ユーザーが最初のテキストをクリックし、Androidロジックからレイアウトを少し上にパンしてから、ここで実装したロジックからスクロールするとします。次に、ユーザーが2番目のビューをクリックすると、Androidロジックが現在のスクロールy値からパンするため、editTextに非常に近くなり、スクロールyが既に増加しているため、ロジックは機能しません。または、スクロールyをインクリメントし続けることもできます。これにより、レイアウトのバランスが崩れ、望ましくなくなります。