Jetpack ComposeでIMEの表示/非表示に合わせてコンポーネントの位置を変更する
デモ
こういうの

ライブラリ
以下のライブラリを導入します。
implementation 'com.google.accompanist:accompanist-insets:0.23.0'
Accompanistは、Jetpack Composeにはまだ実装されていない機能を補完したライブラリ群です。リンクは末尾に掲載しておきます。
実装
まず、AndroidManifestにandroid:windowSoftInputMode="adjustResizeを追加します。
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize">
adjustResizeはソフトウェアキーボードの表示スペースを確保し、Activityのメインウィンドウは動的にサイズが変更されます。このとき他のUIはスクロールできないと全てのコンテンツが見れなくなることがあるため注意しましょう。
どうやら、本来このような設定は不要とのことですが、Jetpack Composeでは、デフォルト設定のstateUnspecifiedだとIMEのアニメーションが機能しないようです。
The default value of windowSoftInputMode should work, but Compose does not currently set the flags necessary.
https://google.github.io/accompanist/insets/#ime-animations
次に、上記で設定したActivityでWindowCompat.setDecorFitsSystemWindows(window, false)を呼び出します。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
}
今回はTextFieldの位置を調整するために、Modifier.padding()を用います。また、この関数の引数にセットするDPはソフトウェアキーボードの表示/非表示に合わせるために以下のように設定します。(数値はお好みでどうぞ)
val configuration = LocalConfiguration.current
val ime = LocalWindowInsets.current.ime
val padding: Dp by animateDpAsState(
targetValue = if (ime.isVisible) {
// 56.dp
TextFieldDefaults.MinHeight
} else {
// Center
(configuration.screenHeightDp / 2).dp
}
)
animateDpAsStateはtargetValueが変更されると自動でアニメーションが開始します。
あとは好きなUIにセットするだけです。(デモではTextFieldを色々カスタマイズしてます)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val configuration = LocalConfiguration.current
val ime = LocalWindowInsets.current.ime
val padding: Dp by animateDpAsState(
targetValue = if (ime.isVisible) {
TextFieldDefaults.MinHeight
} else {
(configuration.screenHeightDp / 2).dp
}
)
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(top = padding)
) {
TextField()
}
}
}
}
リンク
windowSoftInputModeについて
Accompanistについて
Discussion