Jetpack Compose v1.5.0
stable になりました🥳。
今回もたくさんのAPIの追加や更新がありますし、その中で個人的に気になった API を紹介しようと思います。Compose チームもまとめ記事を出しましたので、ぜひ読んでみてください。
今回のリリースでは Android Dev Summit'22 で発表された Modifier.Node の改善を含めて、主に内部的なパフォーマンス向上に向けて更新が多いんですね。
そして、Compose パフォーマンスのテーマで Compose チームのメンバーたちが android developers backstage ポッドキャストもぜひ聴いてみてください。
LookaheadLayout の代わりに LookaheadScope
特に Shared element transition を実現するため追加された Lookahead API について v1.3.0 で追加されたとき軽く紹介書いたことあります。良かったらぜひ読んでみてください。
もっと理解を深めるため Jetpack Compose Internals の筆者 Jorge Castillo の記事もぜひ読んでみてください。
v1.5.0 以降 LookaheadLayout
の代わりに新しい LookaheadScope
を使えるようになってます(v1.6.0 で LookaheadLayout がもう deprecate される動きも見られます)。
// v1.5.0 以下
Column(modifier = Modifier..) {
LookaheadLayout(modifier = Modifier.fillMaxSize()) { /* .. */ }
}
// v1.5.0 以降
Column(modifier = Modifier..) {
LookaheadLayoutScope { /* .. */ }
}
LookaheadLayout は元々 Row
、Column
みたいな layout で、子供たちの MeasurePolicy(サイズ調整と配置)は LookaheadLayout に依存しました。
LookaheadScope は layout ではないので、子供たちの MeasurePolicy は LookaheadScope が追加される layout に依存します。つまり、LookaheadLayout という特別な layout と依存性がなくなります。
そして、Lookahead API で追加された2つの Modifier Modifier.placed
と Modifier.onIntermediatedLayout
でも変更点があります。
Modifier.intermediateLayout
Modifier.intermediateLayout はアニメーション途中のフレームで子供たちの MeasurePolicy(サイズ調整と配置)を計算するためのコールバックです。
ここで lookaheadSize
というアニメーションが終わり次第の子供たちのサイズをパラメータとして以前取得しましたが、これからはコールバックのスコープ IntermediateLayoutScope
のプロパティとして使えるようになってます。
// v1.5.0 以下
Modifier.intermediateLayout { measurable, constraints, lookaheadSize ->
val targetSize = lookaheadSize
/* targetSize までアニメーションを実行する */
}
Modifier.intermediateLayout { measurable, constraints ->
val targeSize = this.lookaheadSize
/* targetSize までアニメーションを実行する */
}
// v1.5.0 以降
Modifier.onPlaced
アニメーションが終わり次第子供たちが配置される位置を Modifier.onPlaced
で予想して調整できてました。このメソッドを使えなくなってます。
このメソッドで取得する lookaheadScopeCoordinates
を Modifier.intermediateLayout
で取得できますが、PlacementScope
にスコープされるようになったため、配置されるフェーズのコールバックの layout{}
のみでアクセスできるようになってます。
// v1.5.0 以下
Modifier.onPlaced { lookaheadScopeCoordinates, layoutCoordinates ->
val targetOffset = lookaheadScopeCoordinates.localLookaheadPositionOf(layoutCoordinates)
/* targetOffset までアニメーションを実行する */
}
// v1.5.0 以降
Modifier.intermediateLayout {
// ここ lookaheadScopeCoordinates 取得できない
..
layout(..) {
coordinates?.let {
val targetOffset = lookaheadScopeCoordinates.localLookaheadPositionOf(coordinates)
/* targetOffset までアニメーションを実行する */
}
}
}
LookaheadScope を使って Lookahead API のサンプルコードが更新されてますので、ぜひみて見てください。
ModalBottomSheet の追加点
これは compose.material3 ではなく compose.material の BottomSheet API のことです。
スワイプジェスチャーを有無にするフラグ
ModalBottomSheet
をユーザー操作でスワイプジェスチャーを有無できるフラグ sheetGestureEnabled
が追加されてます。
ModalBottomSheetLayout(
sheetGestureEnabled = /* デフォルトは true */
)
スクロールの進捗を取得できる
ModalBottomSheetState
で現在の anchor(Hidden
, Expanded
や HalfExpanded
)と最も近い anchor の間の進捗を取得できるため新しいprogress
が追加されました。
BottomSheetState
とBottomDrawerState
でもこの値が追加されてます。
val bottomSheetState = rememberModalBottomSheetState()
ModalBottomSheetLayout(sheetState = state, ..) {
Text("Progress ${state.progress}")
..
}

TextStyle.merge()
パフォーマンス向上に向けた対応で、TextStyle
のすべてのパラメーターを含めた新しいTextStyle.merge()
メソッドが追加されました。
ドキュメンテーションでTextStyle.copy()
よりTextStyle.merge
の利用を推奨されてます。
// v1.5.0 以下
val customStyle = MaterialTheme.typography.bodyLarge.copy(
fontWeight = FontWeight.Bold,
fontSize = 14.sp
)
// v1.5.0 以降
val customStyle = MaterialTheme.typography.bodyLarge.merge(
fontWeight = FontWeight.Bold,
fontSize = 14.sp
)
mutableXXStateOf
v1.5.0
では int
、double
、long
とfloat
タイプの新しい mutableStateXXOf
メソッドが追加されました。
// v1.5.0 以下
var count by remember { mutableStateOf(10) }
var progress by remember { mutableStateOf(0f) }
// v1.5.0 以降
var count by remember { mutableStateIntOf(10) }
var progress by remember { mutableStateFloatOf(0) }
この4つのタイプの場合 mutableStateOf
をそのまま使うと以下のような lint warning が表示されます。
Prefer mutableFloatStateOf instead of mutableStateOf
Inspection info:Calling mutableStateOf() when T is either backed by a primitive type on the JVM or is a value class results in a state implementation that requires all state values to be boxed. This usually causes an additional allocation for each state write, and adds some additional work to auto-unbox values when reading the value of the state.
また Compose のパフォーマンス向上に向けた対応ではないかと考えてます。
この lint warning の ID は AutoboxingStateCreation
と警告レベルが information になってます。もし、無効や警告レベルを上げたいなら以下のように調整できます。
// 無効にする
@Suppress("AutoboxingStateCreation")
@Composable
fun Sample(){..}
// 警告レベルを warning に上げる
// build.gradle
android {
lint {
// build.gradle.ktsの場合
// warning.add("AutoboxingStateCreation")
warning AutoboxingStateCreation
}
}
drawText で BlendMode 追加
Canvas の drawText メソッドで BlendMode
パラメータを渡せるようになってます。
Canvas {
drawText(text = "Blend", ..)
drawText(text = "Exclusion", blendMode = BlendMode.Exclusion, ..)
}

Modifier.pointerInput が lazy になった
API変化がなく、パフォーマンス向上のために内部的な改善で Modifier.pointerInput が lazy になってます。
下記の比較では、v1.5.0以降はユーザーが操作するまで pointerInput{ }
ブロックも実行されないことを確認できますね。
v1.5.0以下 | v1.5.0以降 |
---|---|
![]() |
![]() |
Modifier.pointerInput(Unit) {
// <-- v1.5.0 はここもユーザー操作まで実行されない
awaitPointerEventScope {..}
}
詳しくこの記事を見てみてください。
終わりに
BasicText2、Modifier.Node 改善を含めて他にも、ここに書いてない v1.5.0 のAPI、特にパフォーマンス向上に向けて、更新が沢山あります。次は、9月14-15日 DroidKaigi 2023 ですね!盛り上がりましょう!🥳