Jetpack Compose では、テーマを適用することで、簡単にアプリのデザインに一貫性を持たせることができます。Compose のマテリアル デザインの実装は、プロダクトのブランドに合わせてカスタマイズできます。ニーズに合わない場合は、Compose の公開 API を使用してカスタム デザイン システムを構築できます。
アプリ全体のテーマ設定
Jetpack Compose には、デジタル インターフェースを作成するための包括的なデザイン システムであるマテリアル デザインの実装が用意されています。マテリアル デザインのコンポーネント(ボタン、カード、スイッチなど)は、プロダクトのブランドをより良く反映するようにマテリアル デザインをカスタマイズする体系的な方法である、マテリアル テーマ設定に基づいて構築されています。マテリアル テーマは、色、タイポグラフィ、シェイプの属性で構成されています。これらの属性をカスタマイズすると、その変更内容は、アプリのビルドに使用するコンポーネントに自動的に反映されます。
Jetpack Compose は、MaterialTheme
コンポーザブルを使用して、こうしたコンセプトを実装します。
MaterialTheme(
colors = …,
typography = …,
shapes = …
) {
// app content
}
MaterialTheme
に渡すパラメータを構成して、アプリのテーマを設定します。
図 1. 1 つ目のスクリーンショットは MaterialTheme
を構成していないアプリを示しています。そのため、デフォルトのスタイリングが使用されています。2 つ目のスクリーンショットは、スタイリングをカスタマイズするために MaterialTheme
にパラメータを渡すアプリを示しています。
色
Compose では、色はシンプルなデータ保持クラスである Color
クラスによってモデル化されます。
val red = Color(0xffff0000)
val blue = Color(red = 0f, green = 0f, blue = 1f)
これらはどのように整理してもかまいませんが(最上位の定数として、シングルトン内で、またはインラインで定義)、テーマで色を指定し、そこから色を取得することを強くおすすめします。このアプローチにより、ダークテーマなど、複数のテーマをサポートできるようになります。
Compose には、マテリアル カラーシステムをモデル化するための Colors
クラスが用意されています。Colors
は、明るい色または暗い色のセットを作成するためのビルダー関数を提供します。
private val Yellow200 = Color(0xffffeb46)
private val Blue200 = Color(0xff91a4fc)
// ...
private val DarkColors = darkColors(
primary = Yellow200,
secondary = Blue200,
// ...
)
private val LightColors = lightColors(
primary = Yellow500,
primaryVariant = Yellow400,
secondary = Blue700,
// ...
)
Colors
を定義したら、MaterialTheme
に渡すことができます。
MaterialTheme(
colors = if (darkTheme) DarkColors else LightColors
) {
// app content
}
テーマ色の使用
MaterialTheme.colors
を使用すると、MaterialTheme
コンポーザブルに提供されている Colors
を取得できます。
Text(
text = "Hello theming",
color = MaterialTheme.colors.primary
)
サーフェスとコンテンツの色
多くのコンポーネントは、色と「コンテンツ色」のペアを受け入れます。
Surface(
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
…
TopAppBar(
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
…
これにより、コンポーザブルの色を設定するだけでなく、コンテンツ(コンポーザブルの中に含まれるコンポーザブル)のデフォルト色も指定できます。多くのコンポーザブルは、デフォルトでこのコンテンツ色を使用します。たとえば、Text
の色は親のコンテンツ色に基づき、Icon
はその色を使用して色合いを設定します。
図 2. 異なる背景色を設定すると、テキストとアイコンが異なる色になります。
contentColorFor()
メソッドは、テーマカラーに適した「on」色を取得します。たとえば、primary
背景を設定すると、コンテンツ色として onPrimary
が設定されます。テーマ以外の背景色を設定する場合は、適切なコンテンツ色も指定する必要があります。LocalContentColor
を使用して、現在の背景と対照的な現在のコンテンツ色を取得します。
コンテンツのアルファ版
重要度を伝え、視覚的な階層を作るために、コンテンツを強調する度合いを変化させたいことがよくあります。マテリアル デザインでは、異なるレベルの透明度を利用してさまざまな重要度レベルを示すことを推奨しています。
Jetpack Compose は LocalContentAlpha
を介して、これを実装します。この CompositionLocal
に値を指定することで、階層のコンテンツのアルファ版を指定できます。子のコンポーザブルは、この値を使用できます。たとえば、Text
と Icon
は、LocalContentAlpha
を使用するように調整された LocalContentColor
の組み合わせをデフォルトで使用します。Material は、ContentAlpha
オブジェクトによってモデル化される一部の標準的なアルファ値(high
、medium
、disabled
)を指定します。MaterialTheme
では、LocalContentAlpha
のデフォルトが ContentAlpha.high
に設定されるので注意してください。
// By default, both Icon & Text use the combination of LocalContentColor &
// LocalContentAlpha. De-emphasize content by setting content alpha
Providers(LocalContentAlpha provides ContentAlpha.medium) {
Text(/*...*/)
}
Providers(LocalContentAlpha provides ContentAlpha.disabled) {
Icon(/*...*/)
Text(/*...*/)
}
図 3. テキストにさまざまなレベルの強調を適用して、情報の階層を視覚的に伝えます。
ダークテーマ
Compose では、MaterialTheme
コンポーザブルにさまざまな Colors
のセットを指定し、テーマを通じて色を使用することで、ライトテーマとダークテーマを実装します。
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
colors = if (darkTheme) DarkColors else LightColors,
/*...*/
content = content
)
}
この例では、MaterialTheme
が独自のコンポーズ可能な関数でラップされています。この関数は、ダークテーマを使用するかどうかを指定するパラメータを受け入れます。この場合、関数はデバイスのテーマ設定をクエリすることで、darkTheme
のデフォルト値を取得します。
ダークテーマを実装する際、現在の Colors
が明るいか暗いかを確認できます。
val isLightTheme = MaterialTheme.colors.isLight
この値は、ビルダー関数 lightColors()
と darkColors()
によって設定されます。
マテリアルでは、エレベーションの高いダークテーマのサーフェスは、背景を明るくするエレベーション オーバーレイを受け取ります。こうしたオーバーレイは、暗い色を使用している場合、Surface
コンポーザブルによって自動的に実装されます。
Surface(
elevation = 2.dp,
color = MaterialTheme.colors.surface, // color will be adjusted for elevation
/*...*/
) { /*...*/ }
図 4. カードとボトム ナビゲーションは背景と同じように surface
色が付けられていますが、エレベーションが高いため、色がやや明るくなっています。
マテリアル カラーの拡張
Compose は、マテリアルのカラーテーマ設定を綿密にモデル化して、マテリアル ガイドラインに沿った、シンプルかつタイプセーフなものにします。カラーセットを拡張する必要がある場合は、下記のように独自のカラーシステムを実装するか、拡張機能を追加します。
@Composable
val Colors.snackbarAction: Color
get() = if (isLight) Red300 else Red700
タイポグラフィ
マテリアルはタイプシステムを定義し、意味的に名前をつけたスタイルを少数使用するよう推奨しています。
Compose は、Typography
、TextStyle
、フォント関連のクラスでタイプシステムを実装しています。Typography
コンストラクタは各スタイルのデフォルトを提供するため、カスタマイズしないものは省略できます。
val Rubik = FontFamily(
Font(R.font.rubik_regular),
Font(R.font.rubik_medium, FontWeight.W500),
Font(R.font.rubik_bold, FontWeight.Bold)
)
val MyTypography = Typography(
h1 = TextStyle(
fontFamily = Rubik,
fontWeight = FontWeight.W300,
fontSize = 96.sp
),
body1 = TextStyle(
fontFamily = Rubik,
fontWeight = FontWeight.W600,
fontSize = 16.sp
)
/*...*/
)
MaterialTheme(typography = MyTypography, /*...*/)
全体を通して同じフォントを使用する場合は、defaultFontFamily
パラメータを指定し、TextStyle
要素の fontFamily
を省略します。
val typography = Typography(defaultFontFamily = Rubik)
MaterialTheme(typography = typography, /*...*/)
テキスト スタイルの使用
次の例に示すように、テーマから TextStyle
を取得します。
Text(
text = "Subtitle2 styled",
style = MaterialTheme.typography.subtitle2
)
図 5. 書体とスタイルを使い分けてブランドを表現します。
形
マテリアルはシェイプ システムを定義しており、大、中、小のコンポーネントのシェイプを定義できます。
Compose は Shapes
クラスでシェイプ システムを実装しており、カテゴリごとに CornerBasedShape
を指定できます。
val Shapes = Shapes(
small = RoundedCornerShape(percent = 50),
medium = RoundedCornerShape(0f),
large = CutCornerShape(
topStart = 16.dp,
topEnd = 0.dp,
bottomStart = 16.dp,
bottomEnd = 0.dp
)
)
MaterialTheme(shapes = Shapes, /*...*/)
多くのコンポーネントで、こうしたシェイプがデフォルトで使用されます。たとえば、Button
、TextField
、FloatingActionButton
のデフォルトは small、AlertDialog
のデフォルトは medium、ModalDrawerLayout
のデフォルトは large です。マッピングの詳細については、シェイプ スキームのリファレンスをご覧ください。
シェイプの使用
テーマからシェイプを取得します。
Surface(
shape = MaterialTheme.shapes.medium, /*...*/
) {
/*...*/
}
図 6. シェイプを使用してブランドや状態を表します。
コンポーネント スタイル
Compose にはコンポーネント スタイルの明確な概念はありません。独自のコンポーザブルを作成することでこの機能を提供します。たとえば、ボタンのスタイルを作成するには、独自のコンポーズ可能な関数でボタンをラップし、変更するパラメータを直接設定します。他のパラメータは包含コンポーザブルにパラメータとして公開します。
@Composable
fun LoginButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
Button(
colors = ButtonConstants.defaultButtonColors(
backgroundColor = MaterialTheme.colors.secondary
),
onClick = onClick,
modifier = modifier,
content = content
)
}
カスタム デザイン システム
マテリアルは Google が推奨するデザイン システムであり、Jetpack Compose にはマテリアルの実装が同梱されていますが、これを使用するように制限されているわけではありません。独自のデザイン システムを同じように作成することも十分に可能です。マテリアルは完全に、デザイン システムの作成に使用できる公開 API に基づいています。
カスタム デザイン システムの構築方法に関する詳細な説明は、このドキュメントの対象外ですが、次のリソースを参照してください。
- MaterialTheme ソースコード
- CompositionLocal
- Providers
- Jessnack サンプルアプリ。カスタム カラーシステムを実装しています。
詳細
詳細については、Jetpack Compose テーマ設定の Codelab をご覧ください。