こんにちは!Jetpack Compose と KMM が好きなエンジニアのスージです。
Jetpack Compose v1.4.0
stable になりました。今回もたくさんのAPIの追加や更新がありますし、その中で個人的に気になった API を紹介しようと思います。Compose チームもまとめ記事を出しましたので、ぜひ読んでみてください。
android-developers.googleblog.com
以前 Jetpack Compose v1.3.0
が stable になった時もまとめ記事を書きましたので、良かったらぜひ読んでみてください。
- Pager の Accompanist 卒業
- Flowレイアウト
- marquee アニメーション
- minLines サポート
- LazyList の focus バグ修正と PinnableContainer
- TextMotion
- AndroidView の View の再利用が可能
- Modifier.Node 化の対応
- Material 3
- 終わりに
Pager の Accompanist 卒業
Accompanist ライブラリーから卒業して、Compose 1.4.0 でHorizontalPager
とVerticalPager
が追加されました。
- Accompanist からマイグレーションのガイドが用意されてますので参考にしてください。
- インジケーター対応が入っていないため、カスタムインジケータを実装したくない場合
accompanist-pager-indicators:0.29.2-rc
が既にandroidx.compose.foundation.pager.PagerState
をサポートしてます。使い方についてこの記事を参考にしてください。
val state = rememberPagerState(initialPage = 0) Column { HorizontalPager(pageCount = 4, state = state ..) { //.. ページコンテンツ } // Accompanist インジケータ // pageCount あるメソッドは androidx.compose.foundation.pager.PagerState をサポートしてます HorizontalPagerIndicator(pagerState = state, pageCount = 4 ..) }
Flowレイアウト
スペースがない場合に改行を入れる用の FlowRow と FlowColumn が追加されてます。Row
と Column
と同様で alignment と arrangement を定義でき、各行のアイテムの最大数も定義できます。
FlowRow( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), maxItemsInEachRow = 4 ) { listOf("豚", "姫", "虎", "王", "王子", "お嬢", "ねこ", "ネズミ", "カワウソ").forEach { AssistChip( onClick = { .. }, label = { Text(text = it) } ) } }
marquee アニメーション
marquee アニメーション対応する新しい Modifier.basicMarquee()
が追加されてます。スクロール速度、ループ間の余白、アニメーション delay、回数などを調整できます。
Modifier .basicMarquee( spacing = .., // ループ間のスペース velocity = .., // スクロール速度 iterations = .., // 回数。Int.MAX_VALUE:無限ループ、0:ループしない initialDelayMillis = .., // 初回ループ始まるまでの delay delayMillis = .. // 次のループ始まるまでの delay )
注意点として、コンテンツの利用可能なスペースに収まらない場合のみこのアニメーションが実行されます。収まれたら効かないです。
この Modifier は Text
だけでなく、上記の例のようにどのコンポーネントでも付けることができます。
val transition = rememberInfiniteTransition() val topBannerColor by transition.animateColor(..) Column { Row( modifier = Modifier .basicMarquee( spacing = MarqueeSpacing.fractionOfContainer(0f), velocity = 50.dp, iterations = Int.MAX_VALUE, delayMillis = 0 ) .background(color = topBannerColor) ) { repeat(10) { if (it % 2 == 0) { Text(text = "・JAPAN ARE CHAMPIONS") } else { Text(text = "・WBC 2023") } } } Text( modifier = Modifier .basicMarquee( iterations = Int.MAX_VALUE, spacing = MarqueeSpacing.fractionOfContainer(0.1f), delayMillis = 0 ), text = "SAMURAI JAPAN 3 : 2 TEAM USA" ) }
minLines サポート
Text
と TextField
で minLines サポートが追加されてます。
TextField(.., minLines = 3)
LazyList の focus バグ修正と PinnableContainer
1.4.0 以降 LazyList で focus 持ちの item が viewport から消えたら focus 外されてしまうバグがあって、今回のリリースで修正が入ってます。
1.4.0 以下の focus バグ | 1.4.0 で修正が入ってます |
修正として PinnableContainer という汎用的なピン留め仕組みが追加されてます。LazyList
の場合は、このピン留め仕組みを使って item が viewport から消えても Composition から破棄されないような実装が追加されてます。
これについて調べてみてこの記事でまとめてますので、興味あればぜひ読んでみてください。 goodpatch-tech.hatenablog.com
TextMotion
Text
が画面で固定されたとアニメーションされた場合レンダリングを最適化するために新しいTextMotion
が追加されてます。
早いアニメーションで差分がわかりにくいんですが、以下のようにアニメーションされる Text に TextMotion.Animated
指定しないと Animation がスムーズにレンダリングされない。
アニメーションされる Text に必ず textMotion = TextMotion.Animated
を定義しましょう。
val transition = rememberInfiniteTransition() val fontScale by transition.animateFloat(..) Text( style = TextStyle( textMotion = TextMotion.Animated, // デフォルトは TextMotion.Static fontSize = 12.sp.times(fontScale) ), //.. )
AndroidView の View の再利用が可能
View.java 系のビューを Compose化するための AndroidView
で onReset
と onRelease
2つコールバックが新しく追加されてます。
onReset
: AndroidView が Composition から一時的に破棄や別のところで移動された後、また Composition に追加される時のコールバックです。onRelease
:Composition から完全に破棄された(再利用される可能性がない)時のコールバックです。
@Composable fun <T : View> AndroidView( factory: (Context) -> T, modifier: Modifier = Modifier, onReset: ((T) -> Unit)? = null, onRelease: (T) -> Unit = NoOpUpdate, update: (T) -> Unit = NoOpUpdate )
LazyList のような再利用サポートしてるレイアウトで、この onReset
を実装すれば AndroidView が wrap してる View が再利用されます。注意点としては、onReset が null になった場合、 View が再利用されないです。
サンプル実装も用意されてます。
@Composable fun ReusableAndroidViewInLazyColumnSample() { LazyVerticalGrid(columns = GridCells.Adaptive(512.dp)) { items(urls) { url -> AndroidView( factory = { context -> WebView(context) //.. }, onReset = { webView -> // [1] webView.stopLoading() webView.loadUrl("about:blank") webView.clearHistory() }, update = { // [2] webView.loadUrl(url) } ) } } //.. }
onReset
で再利用される WebView インスタンスをupdate
が呼び出させる前にリセットされてます(url を about:blank と履歴を削除)。- 次呼び出せる
update
で url がまたロードされてます。
Compose node のライフサイクル観察
1.4.0 で新しい ComposeNodeLifecycleCallback
という Compose node のライフサイクルを観察できるような interface が追加されました。
これを使って上記の2つのコールバックが実装されました。内部的な実装について以下のスレッドでまとめてみましたので良ければ読んでみてください。
#JetpackCompose 1.4.0 で LazyList で AndroidView を使う場合スクロールパフォーマンスの向上対応が入ってます。
— すーじ・🍜・🍙 (@_SUR4J_) 2023年3月12日
この対応について少し調べてみました。🧵⬇️ https://t.co/ZrQx5k7M4Q
上記のツイートは正式リリース前の調査なため、一部間違っている箇所があります 🙇♂️
- 「スクロールパフォーマンスの向上対応が入ってます」ではなく、View を再利用してパフォーマンス向上するように実装することもできるようになりました。
Modifier.Node 化の対応
去年の Android Dev Summit で Compose UI のパフォーマンス向上のため Modifier.Node が紹介されました。この API Modifier.Node について少し解釈してみた記事書いたことあるので良かったら読んでみてください。
v1.4.0 で focus、padding、size、drawWithCache などいくつかの Modifier が Modifier.Node 化がされてますので、パフォーマンス向上のために 1.4.0 にアップデートして方がいいと思ってます。API は変わっておらず内部的な実装が変わっているので、バージョンアップすれば大丈夫と思います。
AOSP の Modifier.Node 化の pull request も参考になるかもしれないのでぜひチェックしてみてください。
https://android-review.googlesource.com/q/Modifier.Node
Material 3
Material Design 3 のライブラリーは 1.4.0 ではないんですが、1.3.0 のリリースからいくつかコンポーネント追加されてます。
BottomSheet
Compose Material Design 3 ライブラリの未対応だった bottom sheet コンポーネントが追加されてます。Material Design 2 と 3 で bottom sheet の使い方についてこの記事で簡単にまとめられているので参考にしてください。 www.composables.co
Mobile 勉強会で発表してくれた @RyuNen344さんの資料も参考になります。
本日の資料です!https://t.co/w6PVCVnw5y#mobile勉強会
— りうねん (@RyuNen344) March 7, 2023
Tooltips
Material Design 3 のコンポーネント一覧でまだ Tool tips が入ってないです。
Plain Tooltip | Rich Tooltip |
Time picker と Date picker
Time picker | Date picker | Date range picker |
終わりに
他にも、ここに書いてない 1.4.0 のAPI追加と更新は沢山あります。次は、5月 Google I/O あるので Compose 含めて Android 開発周りの色んな発表楽しみですね!