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

Jetpack Compose Preview実践ガイド

Avatar for kobaken kobaken
September 24, 2025

Jetpack Compose Preview実践ガイド

2025年9月24日(木)開催のAfter DroidKaigi 2025 from ピクシブで発表した資料です。

Avatar for kobaken

kobaken

September 24, 2025
Tweet

More Decks by kobaken

Other Decks in Programming

Transcript

  1. Pros & Cons Pros👍 ・エミュレータ or 実機いらず ・リアルタイムで更新される Cons👎 ・実際とは異なるpadding、

    marginが表示されてしまう ・Coilなどネットワーク経由の画像 表示ができない
  2. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt
  3. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt 表示名
  4. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt 同名のプレビューをまとめる
  5. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt 同名のプレビューをまとめる
  6. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt ComposeをレンダリングするAPI レベル
  7. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt 幅と高さ
  8. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt 言語/地域
  9. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt フォントスケール
  10. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt システムUIの有無 背景色
  11. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt uiMode 夜間モードやTV, VR HEADSETなどが存在する
  12. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt デバイス 組込みのデバイス設定や specによる自由な設定まで
  13. @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION) @Repeatable annotation class Preview( val name:

    String = "", val group: String = "", @IntRange(from = 1) val apiLevel: Int = -1, val widthDp: Int = -1, val heightDp: Int = -1, val locale: String = "", @FloatRange(from = 0.01) val fontScale: Float = 1f, val showSystemUi: Boolean = false, val showBackground: Boolean = false, val backgroundColor: Long = 0, @UiMode val uiMode: Int = 0, @Device val device: String = Devices.DEFAULT, @Wallpaper val wallpaper: Int = Wallpapers.NONE, ) Preview.android.kt ダイナミックカラー Compose 1.4.0↑
  14. @Preview( name = "light", group = "uiMode", showBackground = true,

    uiMode = UI_MODE_NIGHT_NO, backgroundColor = 0xFFFFFFFF, ) @Preview( name = "night", group = "uiMode", showBackground = true, uiMode = UI_MODE_NIGHT_YES, backgroundColor = 0xFF1F1F1F, ) annotation class DarkLightPreview DarkLightPreview.kt
  15. import coil.compose.AsyncImage @Preview @Composable private fun AsyncImagePreview() { AsyncImage( model

    = "https://github.com/kobaken0029.png", contentDescription = null, ) } AsyncImagePreview.kt
  16. R.drawableを使う sealed interface ImageDataSource { val data: Any data class

    Url(override val data: String): ImageDataSource data class File(override val data: File): ImageDataSource data class Drawable(@DrawableRes override val data: Int) : ImageDataSource } ImageDataSource.kt
  17. R.drawableを使う fun CustomImage( modifier: Modifier = Modifier, dataSource: ImageDataSource, )

    { AsyncImage( modifier = modifier, model = ImageRequest.Builder(LocalContext.current) .data(dataSource.data) .build(), … ) } CustomImage.kt
  18. import coil.compose.AsyncImage @Preview @Composable private fun CustomImagePreview() { CustomImage( dataSource

    = ImageDataSource .Drawable(R.drawable.naiyo) ) } CustomImagePreview.kt
  19. import coil.compose.AsyncImage @Preview @Composable private fun AsyncImagePreview() { AsyncImage( model

    = "https://github.com/kobaken0029.png", contentDescription = null, ) } AsyncImagePreview.kt
  20. まとめ ・LayoutlibによってAndroid Studio上でのプレビューを実現している ・@Previewのパラメータを変更して見た目や振る舞いを変更できる ・fontScale, local, uiMode, etc… ・@Previewをカスタムしたり、マルチプレビューテンプレ使用で効率UP ・Run

    Previewすることでネットワーク経由の画像表示にも対応可能 ・適切にモジュール分割をしてプレビューを快適に ・CMPでもプレビューは動く(機能が少ない、一部表現できない) ・androidMainでandroidx製の@Previewを利用可能
  21. 引用・参照元 ・Jetpack Compose UI App Development Toolkit ・Compose Multiplatform 1.6.0

    – リソース、UI テスト、iOS アクセシビリ ティ、および Preview アノテーション | The Kotlin Blog ・PreviewActivityをHackする #Android - Qiita ・Compose駆動開発のためのマルチモジュール化 - Speaker Deck