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) }
関連記事: