Jetpack Compose:画面のプレビュー(@Preview)

投稿日:  更新日:

Android StudioにおけるJetpack Composeプロジェクトは、エディタ上でUIのプレビューが行えます。

Kotlinで記述した画面構成(UIツリー)が視覚的に確認できるので、とても便利です。

さらに、色々な表示条件の設定が行えるので、使いこなせば更に利便性が向上します。

※この記事の執筆中にドキュメント「コンポーザブルのプレビューで UI をプレビューする」を見つけました。記事はこのドキュメントと重複する部分が多いです。ドキュメントも参考にして下さい。

※環境:Android Studio Giraffe | 2022.3.1 Patch 3
    Kotlin 1.8.10
    Compose Compiler 1.4.3

スポンサーリンク

画面のプレビュー

@Previewアノテーションの付いたComposable関数はプレビュー対象になります。

プレビュー対象の関数は、Android StudioによりUIの状態が表示されます。ただし、表示にはビルドが必要です。

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {			// <- 引数を持ていない
    MyApplicationTheme {
        Greeting("Android")
    }
}
enum class GreetingType(val mesgId: Int) {
    Morning(R.string.greeting_morning),
    Daytime(R.string.greeting_daytime),
    Evening(R.string.greeting_evening)
}

@Composable
fun Greeting(
    name: String, type: GreetingType = GreetingType.Daytime,
    modifier: Modifier = Modifier
) {
    val _greeting = LocalContext.current.resources.getString(type.mesgId)
    Text(
        text = "%s %s!".format(_greeting, name),
        modifier = modifier
    )
}

画面のプレビュー

また、一つのファイル(XXX.kt)で複数のプレビューを同時に行うことができます。

しかし、あまり多くのプレビューを行うと、ビルドに時間がかかり、Android Studioの軽快な操作感が損なわれます。

そのような場合はファイルを分割するとよいでしょう!

スポンサーリンク

プレビューの引数

アノテーション@Previewは引数を用いて、表示条件の設定が行えます。

@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,
    // TODO(mount): Make this Dp when they are inline classes
    val widthDp: Int = -1,
    // TODO(mount): Make this Dp when they are inline classes
    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,
)
引数概要
nameStringプレビューの表示名
・表示パネル内で名前による識別が可能
groupStringプレビューのグループ名
・グルーブにより表示の選択が可能
apiLevelInt描画で使用されるAPIレベル(デフォルト:compileSdkの値)
widthDpIntプレビューの幅
-1:コンテンツ自身のサイズ
heightDpIntプレビューの高さ
-1:コンテンツ自身のサイズ
localeString端末のロケール(言語、地域)
fontScaleFloatフォントのスケーリング係数
showSysUiBooleanスタータスバー・アクションバーの有無
・false:無 / true:有
showBackgroundBoolean背景の有無
・false:無 / true:有
backgroundColorLong背景色
・ARGBカラー整数
uiModeIntUIモードによる端末の構成(ハードの種類、昼/夜モード)
・Configuration.UI_***で定義
deviceStringプレビューで使用するデバイス
・Hardware Profileのnameで指定
・デバイスのスペックを直に指定
wallpaperIntダイナミックカラーを決める壁紙の色合い
スポンサーリンク

プレビューの設定パネル

表示条件の設定はパネルから行えます。Android Studioのエディタに現れる歯車アイコンをクリックして下さい。

PreviewConfigurationパネル

引数で指定可能なパラメータが列記され、値の候補が表示されるようになっています。とても便利です。

スポンサーリンク

パラメータ:name

プレビューに名前を付けます。名前は関数名の隣に表示されます。

@Preview(name = "Default", showBackground = true)
@Composable
fun DaytimeGreeting() {
    Greeting("Android", GreetingType.Daytime)
}

nameの例

マルチプレビュー(後述)を用いた時にnameは役立ちます。nameにパラメータの説明を含めれば、プレビューの概要を通知できます。

@Preview(name = "Default", showBackground = true)
@Preview(name = "Japanese", locale = "ja", showBackground = true)
@Composable
fun LocaleGreeting() {
    Greeting("Android", GreetingType.Daytime)
}

nameの例(マルチプレビュー)

パラメータ:group

プレビューのグループを作ります。
※このサンプルはマルチプレビュー(後述)を用いています。

private const val Target = "Android"

@Preview(group = "Default", showBackground = true)
@Preview(group = "Japanese", locale = "ja", showBackground = true)
@Composable
private fun MorningGreeting() {
    Greeting(Target, GreetingType.Morning)
}

@Preview(group = "Default", showBackground = true)
@Preview(group = "Japanese", locale = "ja", showBackground = true)
@Composable
private fun DaytimeGreeting() {
    Greeting(Target, GreetingType.Daytime)
}

右上にプルダウンリストが出現し、表示するグループが選択できるようになります。

groupの例

groupの例

スポンサーリンク

パラメータ:widthDp,heightDp

プレビューのサイズ(幅:widthDp、高さ:heightDp)を指定します。

private const val Target = "Android"

@Preview(widthDp = 160, heightDp = 80, showBackground = true)
@Composable
fun Greeting_160x80() {
    Greeting(Target, GreetingType.Morning)
}

@Preview(widthDp = 100, heightDp = 50, showBackground = true)
@Composable
fun Greeting_100x50() {
    Greeting(Target, GreetingType.Morning)
}

widthDp, heightDpの例

スポンサーリンク

パラメータ:locale

端末のロケールを指定します。プレビューはロケールに従った言語・単位が使われた表示になります。

<resources>
    <string name="app_name">Preview Sample</string>
    <string name="greeting_daytime">Hello</string>
    <string name="greeting_morning">Good Morning</string>
    <string name="greeting_evening">Good Evening</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Preview Sample</string>
    <string name="greeting_daytime">こんにちは</string>
    <string name="greeting_morning">おはよう</string>
    <string name="greeting_evening">こんばんは</string>
</resources>
private const val Target = "Android"

@Preview(showBackground = true)
@Composable
fun Greeting_Def() {
    Greeting(Target, GreetingType.Daytime)
}

@Preview(locale = "ja", showBackground = true)
@Composable
fun Greeting_Ja() {
    Greeting(Target, GreetingType.Daytime)
}

localeの例

スポンサーリンク

パラメータ:fontScale

プレビューで使用されるフォントのスケールを指定します。

private const val Target = "Android"

@Preview(showBackground = true)
@Composable
fun Greeting_FontScale1_0() {
    Greeting(Target, GreetingType.Daytime)
}

@Preview(fontScale = 0.8f, showBackground = true)
@Composable
fun Greeting_FontScale0_8() {
    Greeting(Target, GreetingType.Daytime)
}

@Preview(fontScale = 1.2f, showBackground = true)
@Composable
fun Greeting_FontScale1_2() {
    Greeting(Target, GreetingType.Daytime)
}

fontScaleの例

スポンサーリンク

パラメータ:showSysUi

プレビューへステータスバーを表示します。

@Preview(showSystemUi = true, showBackground = true)
@Composable
fun Greeting_SysUI() {
    Greeting("Android", GreetingType.Daytime)
}

device(後述)の指定がない場合、デフォルトで「pixel 5」が選ばれます。

showSysUiの例

スポンサーリンク

パラメータ:device

プレビューで使用するデバイスを指定します。

デバイスとは、ハードの種類(Phone,Watch,TV,…、スペック違いの端末)のことです。

deviceは「showSysUi = true」とペアで指定する必要があります。

また、デバイスの指定は、2通りの方法があります。

idで指定

Hardware Profilesのnameがidです。既存のHardware Profileから選びます。

Hardware Profileのname

@Preview(
    showBackground = true,
    showSystemUi = true,
    device = "id:3.2in QVGA (ADP2)"
)
@Composable
fun Greeting_Device() {
    Greeting("Android", GreetingType.Daytime)
}

deviceの例

ユーザが作成したHardware Profileであっても、Android Studioが存在を認識していれば選択できます。

specで指定

デバイスのスペックを直に指定します。

@Preview(
    showBackground = true,
    showSystemUi = true,
    device = "spec:width=320px,height=320px,dpi=160"
)
@Composable
fun Greeting_Device() {
    Greeting("Android", GreetingType.Daytime)
}

deviceの例

スポンサーリンク

パラメータ:showBackground,backgrundColor

プレビューの背景の有無と色を指定します。

private const val Target = "Android"

@Preview(showBackground = true, backgroundColor = 0xFFEAF5D3)
@Composable
fun Greeting_BgTrue() {
    Greeting(Target, GreetingType.Daytime)
}

@Preview(showBackground = false, backgroundColor = 0xFFEAF5D3)
@Composable
fun Greeting_BgFalse() {
    Greeting(Target, GreetingType.Daytime)
}

バックグラウンドの例

スポンサーリンク

パラメータ:uiMode

プレビューのUIモードを指定します。

UIモードはハードの種類(Phone,Watch,TVなど)や夜と昼など、UIに関する端末の構成と変化(Configuration.UI_***)を表現するパラメータの一つです。

ただし、uiModeで指定できるのは、システム内の状態です。

以下は、夜間(ダーク)と昼間(ライト)に適したテーマを指定する例です。

uiModeの指定を活かすには、システム内の状態を読み出し(isSystemInDarkTheme)て、「テーマの選択」が必要になります。

※「テーマの選択」については「Jetpack Composeプロジェクトのテーマ指定」を参照

private const val Target = "Android"

@Preview(
    name = "Night:NO",
    showBackground = true
)
@Preview(
    name = "Night:YES",
    showBackground = true,
    uiMode = Configuration.UI_MODE_NIGHT_YES
)
@Composable
fun Greeting_UiMode() {
    MyApplicationTheme() {	// テーマの選択が行われる
        Surface(
            color = MaterialTheme.colorScheme.background
        ) {
            Column {
                Greeting(Target, GreetingType.Daytime)
                Button(
                    onClick = { /*TODO*/ },
                    modifier = Modifier.align(Alignment.End)
                ) {
                    Text(text = "OK")
                }
            }
        }
    }
}

uiModeの例

スポンサーリンク

パラメータ:wallpaper

プレビューの壁紙を指定します。しかし、実際に壁紙が表示されるわけではありません。壁紙に合わせたアプリの色合い(ダイナミックカラー)が変更されます。

ただし、wallpaperで指定できるのは、システム内の状態です。

wallpaperの指定を活かすには、システム内の状態を読み出し(dynamicTonalPalette)て、「テーマの選択」が必要になります。

※「テーマの選択」については「Jetpack Composeプロジェクトのテーマ指定」を参照

private const val Target = "Android"

@Preview(
    name = "Bg:None", wallpaper = Wallpapers.NONE,
    showBackground = false)
@Preview(
    name = "Bg:Red", wallpaper = Wallpapers.RED_DOMINATED_EXAMPLE,
    showBackground = false)
@Preview(
    name = "Bg:Green", wallpaper = Wallpapers.GREEN_DOMINATED_EXAMPLE,
    showBackground = false)
@Preview(
    name = "Bg:Blue", wallpaper = Wallpapers.BLUE_DOMINATED_EXAMPLE,
    showBackground = false)
@Preview(
    name = "Bg:Yellow", wallpaper = Wallpapers.YELLOW_DOMINATED_EXAMPLE,
    showBackground = false)
@Composable
fun Greeting_Wallpaper() {
    MyApplicationTheme() {	// テーマの選択が行われる
        Surface(
            color = MaterialTheme.colorScheme.background
        ) {
            Column {
                Greeting(Target, GreetingType.Daytime)
                Button(
                    onClick = { /*TODO*/ },
                    modifier = Modifier.align(Alignment.End)
                ) {
                    Text(text = "OK")
                }
            }
        }
    }
}

wallpaperの例

スポンサーリンク

表示条件を変えて複数のプレビュー(マルチプレビュー)

プレビュー対象のComposable関数に、複数の@Previewを対応させることができます。

方法は簡単で、@Previewを列記するだけです。

ここで、各@Previewの引数へ異なる表示条件を与えれば、一度に複数のプレビューを表示できます。

@Preview(name = "Default", showBackground = true)
@Preview(name = "Japanese", locale = "ja", showBackground = true)
@Composable
fun LocaleGreeting() {
    Greeting("Android", GreetingType.Daytime)
}

nameの例(マルチプレビュー)

さらに、マルチプレビューのカスタムアノテーションを作成しておけば、再利用が可能になります。

@Preview(name = "Default", showBackground = true)
@Preview(name = "Japanese", locale = "ja", showBackground = true)
annotation class LocalePreview

@LocalePreview
@Composable
fun DaytimeGreetingPreview() {
    Greeting("Android", GreetingType.Daytime)
}

@LocalePreview
@Composable
fun MorningGreetingPreview() {
    Greeting("Android", GreetingType.Morning)
}

カスタムアノテーションの例

スポンサーリンク

UIの状態を変えて複数のプレビュー(データセット)

UIの状態(Composable関数の引数)を変えて、一度に複数のプレビューしたい場合があります。

このような時、PreviewParameterProviderインターフェースと@PreviewParameterを使います。

class TargetParameter : PreviewParameterProvider<String> {  // データセットの定義
    override val values = sequenceOf(
        "Android",
        "Windows",
        "Linux"
    )
}

@Preview
@Composable
fun TargetPreview(
    @PreviewParameter(TargetParameter::class) target: String  // データセットの指定
) {
    Greeting(target, GreetingType.Daytime)
}

データセットの例

スポンサーリンク

関連記事:

Jetpack Composeが提供する既存のUI要素(Compose UI)は、必ずModifierを引数に持ちます。 このModifierの役割はUI要素へ装飾や機能拡張を追加することですが、裏でアプリ画面の描画処理と密接に関連しており、UI要素よりもシステム側に近い存在です。 理解せずに誤った使い方をすれば、装飾や機能拡張の域を脱してUI要素が表示されないこともあり、思ったようなアプリ画面は望めません。 Modifierについて、まとめます。 ※環境:Android Studio Hedgehog | 2023.1.1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
「Android Studio Giraffe」の作成するプロジェクトは、Jetpack Composeの利用が推奨されます。 そして、作成されたプロジェクトは、Material Designeに準拠したテーマが指定されます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
「Android Studio Giraffe」の作成するプロジェクトは、Jetpack Composeの利用が推奨されます。 今後、Viewシステムに代わり、Jetpack Composeが主流になるようです。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
CompositionLocalはシステムにより提供されているものがあります。 プログラミングに有益で利用頻度の高いものが用意されています。例えば、LocalContextやLocalConfigurationなどです。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
CompositionLocalはUIツリーにローカルな変数を確保します。 その変数の参照キーはcompositionLocalOf関数(以降、Dynamic側と呼ぶ)によって返されるCompositionLocalインスタンスです。 このインスタンスを返す方法に、もう一つ、staticCompositionLocalOf関数(以降、Static側と呼ぶ)があります。 この両者の違いをまとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
CompositionLocalはUIツリーで発生する状態のバケツリレーを解決してくれます。 また、広く共有したい状態の定義にも適しています。例えば、「ツリーのある階層以下に対して」などと言った場合です。 CompositionLocalについて、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
再Composeのスケジューリング(mutableStateOf)はアプリのパフォーマンスに直結する動作です。 状態の保持(remember)に並び、Jetpack Composeの重要な技術の一つです。 今回は「再Composeのスケジューリング」について、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
状態の保持(remenber)はアプリの画面(UI)を制御・管理するために必要な動作です。 再Composeのスケジューリング(mutableStateOf)に並び、Jetpack Composeの重要な技術の一つです。 今回は「再Composeを超えて状態の保持」について、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
mutableStateOfはComposable関数ではありません。 ですので、Composable関数内にある必要はなく、どこでも記述できます。 Activityから表示の更新を発行する方法として使えそうです。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
サンプルアプリを作成して、描画処理(再Compose)の周期を観測してみました。 その結果を紹介します。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack Composeは描画処理の軽量化(消費リソース量の削減)をするために、表示の変更されたUI要素のみを再Composeし、表示の変わらないUI要素をスキップします。これにより、高い表示パフォーマンスを維持しています。 しかし、スキップが正常に行われないとしても、アプリの画面に現れて来ません。なぜなら、同じ表示を無駄に繰り返すことになるからです。 アプリは動くけれど動作が鈍いならば、真っ先に疑うポイントです。不要な再Composeが行われている可能性が考えられます。 これは気付かないうちに蓄積し易い不具合です。ですので、再Composeとスキップについて理解し、予防に努めることをお勧めします。 今回は「再Composeとスキップ」について、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack ComposeはAndroidシステムの新たなUIフレームワークです。従来のViewシステムと、アプリ画面の描画の仕組みが異なります。 このJetpack Composeによるアプリ画面の描画について、仕組みの大枠をまとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
表示の変わらないUI要素(Composable関数)に対して行われる再Composeは無駄な処理です。 ですので、極力排除したいところですが、表示が変わらないため、画面上からの判断が難しくなっています。 このようなとき、Layout Inspectorを利用すると、無駄な再Composeを見つけ出すことが出来ます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1 ...
プロジェクトのビルドで「Something went wrong while checking for version compatibility between the Compose Compiler and the Kotlin Compiler.」とメッセージを吐き、エラーになる場合があります。 既存のプロジェクトを新しくリリースされたAndroid Studioでビルドした場合に頻発します。 先日、「Giraffe|2022.3.1」がリリース(2023.07)されて、早速、ビルドをしたら発生しました。 その対処方法を説明します。 ※環境:Android Studio Giraffe | 2022.3.1 ...
スポンサーリンク