1. この記事の目的
チャットアプリのように、LazyColumnの最下段アイテムを
IME(ソフトウェアキーボード)の開閉に合わせて自然に追従させたい。
この挙動を実現する方法として、offset方式 と scroll方式 の2種類を比較した。
2. 方式1:offset方式
▼ビュー全体を持ち上げるシンプルな方法
IMEのbottom insetを取得して、その分だけビューを持ち上げる方法。
LazyColumn全体とbottomBarを同じ量だけoffsetするだけなので、実装はかなり簡単。
val imeInsetPx = WindowInsets.ime
.exclude(WindowInsets.systemBars).getBottom(density)
val imeInsetDp = with(density) { imeInsetPx.toDp() }
bottomBar側はこう動かす:
bottomBar = {
Box(
Modifier
.fillMaxWidth()
.navigationBarsPadding()
.offset(y = -imeInsetDp) // レイアウト層ではないので
// innerPaddingに影響しない
) {
InputBar { text -> messages.add(text) }
}
}
LazyColumn側も同じようにoffsetする:
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.offset(y = -imeInsetDp),
state = listState
) {
items(messages) { msg ->
MessageBubble(text = msg)
}
}
ビュー全体を持ち上げるだけなので、実装は簡単。
3. 方式2:scroll方式
▼IMEが押し上げた分だけLazyColumnへそのまま反映させる方法
IMEの表示・非表示で描画領域が押し上げられた量(inset bottomの変化量)を
LazyColumnのスクロール量としてそのまま流し込む方法。
端末差やinsetの仕様により誤差が発生しやすいため、補正を入れながら動作させる。
AndroidView(
modifier = Modifier
.fillMaxSize()
.alpha(0f), // 完全透過(当たり判定なし)
factory = { context ->
View(context).apply {
ViewCompat.setWindowInsetsAnimationCallback(
this,
object: WindowInsetsAnimationCompat.Callback(
DISPATCH_MODE_CONTINUE_ON_SUBTREE
) {
override fun onPrepare(
animation: WindowInsetsAnimationCompat
) {
super.onPrepare(animation)
// 初期化(前回のfractionやinsetのリセット)
}
override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: List<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
// inset bottomの変化量(delta)を計算して
// LazyColumnへdispatchRawDelta()として流す
return insets
}
override fun onEnd(animation: WindowInsetsAnimationCompat) {
super.onEnd(animation)
// 最終フレームの補正
}
}
)
}
}
)
描画遅延を避けるため、AndroidViewで全ての処理を行う。
4. 比較まとめ
| offset方式 | scroll方式 | |
|---|---|---|
| 実装難易度 | ★☆☆ 簡単 | ★★★ 難しい |
| 端末差の影響 | ほぼ受けない (Composeが保証) |
受けやすい (補正必須) |
| 誤差の出やすさ | 出ない (毎フレーム最新値) |
出る (currentInsetの更新タイミング差) |
| 方式の性質 | 最新の inset を そのまま使う方式 |
押し上げ量の差分(delta)を そのままスクロール量に使う方式 |
| 総評 | 手軽で安定。まずはこれ。 | 差分方式で正確だが扱いが難しい。 補正必須。 |
難易度:offset方式 <<<<<(超えられない壁)<<<<< scroll方式
5. まとめ
IME追従には offset方式 と scroll方式 の2つがある。
-
offset方式は最新のinsetをそのまま使うシンプルな方法で、
実装が簡単で安定している。 -
scroll方式は押し上げ量の差分(delta)を使う方式で、
正確だが端末差の影響を受けやすく、補正が必要になる。
まずは offset方式 を使うのが無難。
scroll方式は、より細かい制御が必要な場合に選択する。
scroll方式は試行錯誤が多いので、実装するときは覚悟して挑んでください。
俺はもう だ め ぽ ![]()