Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jetpack Composeにおける画像ライブラリ ~Coilに🤏だけ詳しくなる~/ #ni...

Jetpack Composeにおける画像ライブラリ ~Coilに🤏だけ詳しくなる~/ #nikkei_tech_talk

2023/06/15開催、NIKKEI Tech Talk #8登壇資料です #nikkei_tech_talk
タイトルは「Jetpack Composeにおける画像ライブラリ ~Coilに🤏だけ詳しくなる~」です
https://nikkei.connpass.com/event/284090/

More Decks by 日本経済新聞社 エンジニア採用事務局

Other Decks in Technology

Transcript

  1. ハッシュタグ #nikkei_tech_talk Jetpack Compose における画像ライブラリは2択 インターネットから画像を読み込みたい 4 ライブラリ Coil Glide

    github https://github.com/coil-kt/coil https://github.com/bumptech/glide 最新 io.coil-kt:coil-compose:2.4.0 com.github.bumptech.glide:compose: 1.0.0-alpha.3 特徴 高速、軽量、使いやすい、モダン 滑らかなスクロールと速度を 最重視して開発されている 豆知識 💡 Coroutine Image Loader の 頭文字から命名 開発を行っていたBumpをGoogleが買収済み (READMEの免責事項には、 ”This is not an official Google product.” と記載...)
  2. ハッシュタグ #nikkei_tech_talk Coil 使用するAPIは3種類 • AsyncImage • SubcomposeAsyncImage • AsyncImagePainter

    ◦ 低レベルAPI ◦ AsyncImageもSubcomposeAsyncImageも 内部的にはAsyncImagePainterを使用している 6
  3. ハッシュタグ #nikkei_tech_talk Coil 〜AsyncImage〜 • 非同期的にリクエストと描画を行うAPI • foundationのImageコンポーザブルとほぼ似た使い勝手 + placeholderやエラー画像などをPainterで指定したり、

    読み込み状態の変更を受け取ったりできる 7 AsyncImage( model = ImageRequest.Builder(LocalContext.current) .data("https://example.com/image.jpg") .crossfade(true) .build(), placeholder = painterResource(R.drawable.placeholder), contentDescription = stringResource(R.string.description), contentScale = ContentScale.Crop, modifier = Modifier.clip(CircleShape) )
  4. ハッシュタグ #nikkei_tech_talk 【AsyncImageとの違いをざっくり理解】 • AsyncImage:placeholder / error / fallbackをPainterでセット •

    SubcomposeAsyncImage: placeholder / error / fallbackをComposable関数でセット(Slot API) Coil 〜SubcomposeAsyncImage〜 8 SubcomposeAsyncImage( model = "https://example.com/image.jpg", contentDescription = stringResource(R.string.description) ) { val state = painter.state if (state is AsyncImagePainter.State.Loading || state is AsyncImagePainter.State.Error) { CircularProgressIndicator() } else { SubcomposeAsyncImageContent() } }
  5. ハッシュタグ #nikkei_tech_talk 【もう少し詳しく理解】 • SubcomposeLayoutを利用したAsyncImage Coil 〜SubcomposeAsyncImage〜 9 • サイズの計算はLayoutステップの中で行われている

    • SubcomposeLayoutでは、Compositionステップを親のLayoutステップまで 遅らせることで、constraints(制約≒サイズ)の取得を行える https://developer.android.com/jetpack/compose/layouts/basics
  6. ハッシュタグ #nikkei_tech_talk Coil 〜Subcompose AsyncImage〜 10 @Composable fun SubcomposeAsyncImage( model:

    Any?, contentDescription: String?, imageLoader: ImageLoader, modifier: Modifier = Modifier, transform: (State) -> State = DefaultTransform, onState: ((State) -> Unit)? = null, alignment: Alignment = Alignment.Center, contentScale: ContentScale = ContentScale.Fit, alpha: Float = DefaultAlpha, colorFilter: ColorFilter? = null, filterQuality: FilterQuality = DefaultFilterQuality, content: @Composable SubcomposeAsyncImageScope.() -> Unit, ) { ... if (sizeResolver !is ConstraintsSizeResolver) { Box(...) { ... } // ※2 } else { BoxWithConstraints( modifier = modifier, contentAlignment = alignment, propagateMinConstraints = true ) { ... } } } SubcomposeAsyncImageの中身を 覗いてみると...👀 BoxWithConstraints(※1) が使われている! ※1:SubcomposeLayoutを利用したComposable ※2:厳密にはSubcomposeAsyncImageでも、 ImageRequestにサイズを明示的に指定した場合には、 constraints取得が不要なためSubcompositionを 使用しない
  7. ハッシュタグ #nikkei_tech_talk AsyncImage vs SubcomposeAsyncImage Q. Subcomposition、強そうなのでとりあえず使っておけば良い? A. No 🥺

    Subcompositionは拡張性は高いが、パフォーマンスコスト大 • painterで事足りるケースではAsyncImageを使用 • Success, Loading, Errorといった描画状態ごとにComposableを用いた表現を行 いたい場合は、SubcomposeAsyncImageを使用💯 ただし、パフォーマンスに注意(リスト中に多用しないなど) 11
  8. ハッシュタグ #nikkei_tech_talk placeholder表示にあたり、端末の横幅(親レイアウトのサイズ)が必要 🤔 日経での導入談 13 SubcomposeAsyncImage( modifier = modifier.padding(

    … ) .aspectRatio((featuredContentsImage.width / featuredContentsImage.height).toFloat()), model = ImageRequest.Builder(LocalContext.current).data(featuredContentsImage.url).build(), contentDescription = featuredContentsBanner.alt, ) { if (painter.state is AsyncImagePainter.State.Success) { SubcomposeAsyncImageContent() } else { Image( modifier = Modifier.border(BorderStroke(1.dp, colorResource(id = R.color.placeholder_border))), painter = painterResource(id = placeHolder), contentDescription = featuredContentsBanner.alt, ) } }
  9. ハッシュタグ #nikkei_tech_talk まとめ • 画像読み込みライブラリ for Compose:Coil vs Glide •

    CoilのAsyncImage vs SubcomposeAsyncImage • SubcomposeAsyncImageの使い所の見極めが大事👀 • NIKKEIでは、新しいUIにはComposeを積極採用するなど モダンな技術を積極的に取り入れている 14