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,
)
| 引数 | 概要 | |
|---|---|---|
| name | String | プレビューの表示名 ・表示パネル内で名前による識別が可能 |
| group | String | プレビューのグループ名 ・グルーブにより表示の選択が可能 |
| apiLevel | Int | 描画で使用されるAPIレベル(デフォルト:compileSdkの値) |
| widthDp | Int | プレビューの幅 -1:コンテンツ自身のサイズ |
| heightDp | Int | プレビューの高さ -1:コンテンツ自身のサイズ |
| locale | String | 端末のロケール(言語、地域) |
| fontScale | Float | フォントのスケーリング係数 |
| showSysUi | Boolean | スタータスバー・アクションバーの有無 ・false:無 / true:有 |
| showBackground | Boolean | 背景の有無 ・false:無 / true:有 |
| backgroundColor | Long | 背景色 ・ARGBカラー整数 |
| uiMode | Int | UIモードによる端末の構成(ハードの種類、昼/夜モード) ・Configuration.UI_***で定義 |
| device | String | プレビューで使用するデバイス ・Hardware Profileのnameで指定 ・デバイスのスペックを直に指定 |
| wallpaper | Int | ダイナミックカラーを決める壁紙の色合い |
プレビューの設定パネル
表示条件の設定はパネルから行えます。Android Studioのエディタに現れる歯車アイコンをクリックして下さい。

引数で指定可能なパラメータが列記され、値の候補が表示されるようになっています。とても便利です。
パラメータ:name
プレビューに名前を付けます。名前は関数名の隣に表示されます。
@Preview(name = "Default", showBackground = true)
@Composable
fun DaytimeGreeting() {
Greeting("Android", GreetingType.Daytime)
}

マルチプレビュー(後述)を用いた時にnameは役立ちます。nameにパラメータの説明を含めれば、プレビューの概要を通知できます。
@Preview(name = "Default", showBackground = true)
@Preview(name = "Japanese", locale = "ja", showBackground = true)
@Composable
fun LocaleGreeting() {
Greeting("Android", GreetingType.Daytime)
}

パラメータ: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)
}
右上にプルダウンリストが出現し、表示するグループが選択できるようになります。


パラメータ: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)
}

パラメータ: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)
}

パラメータ: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)
}

パラメータ:showSysUi
プレビューへステータスバーを表示します。
@Preview(showSystemUi = true, showBackground = true)
@Composable
fun Greeting_SysUI() {
Greeting("Android", GreetingType.Daytime)
}
device(後述)の指定がない場合、デフォルトで「pixel 5」が選ばれます。

パラメータ:device
プレビューで使用するデバイスを指定します。
デバイスとは、ハードの種類(Phone,Watch,TV,…、スペック違いの端末)のことです。
deviceは「showSysUi = true」とペアで指定する必要があります。
また、デバイスの指定は、2通りの方法があります。
idで指定
Hardware Profilesのnameがidです。既存のHardware Profileから選びます。

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

ユーザが作成した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)
}

パラメータ: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")
}
}
}
}
}

パラメータ: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")
}
}
}
}
}

表示条件を変えて複数のプレビュー(マルチプレビュー)
プレビュー対象のComposable関数に、複数の@Previewを対応させることができます。
方法は簡単で、@Previewを列記するだけです。
ここで、各@Previewの引数へ異なる表示条件を与えれば、一度に複数のプレビューを表示できます。
@Preview(name = "Default", showBackground = true)
@Preview(name = "Japanese", locale = "ja", showBackground = true)
@Composable
fun LocaleGreeting() {
Greeting("Android", GreetingType.Daytime)
}

さらに、マルチプレビューのカスタムアノテーションを作成しておけば、再利用が可能になります。
@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)
}

関連記事:
