10
4

More than 1 year has passed since last update.

Jetpack Compose v1.5.0に気になったAPIのまとめ

Last updated at Posted at 2023-08-10

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 は元々 RowColumn みたいな layout で、子供たちの MeasurePolicy(サイズ調整と配置)は LookaheadLayout に依存しました。
LookaheadScope は layout ではないので、子供たちの MeasurePolicy は LookaheadScope が追加される layout に依存します。つまり、LookaheadLayout という特別な layout と依存性がなくなります。

そして、Lookahead API で追加された2つの Modifier Modifier.placedModifier.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 で予想して調整できてました。このメソッドを使えなくなってます
このメソッドで取得する lookaheadScopeCoordinatesModifier.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, ExpandedHalfExpanded)と最も近い anchor の間の進捗を取得できるため新しいprogressが追加されました。

BottomSheetStateBottomDrawerStateでもこの値が追加されてます。

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 では intdoublelongfloatタイプの新しい 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 ですね!盛り上がりましょう!🥳

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4