Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。
そのライブラリ中のSurfaceについて、構成や使用方法などをまとめます。
※環境:Android Studio Flamingo | 2022.2.1
:androidx.compose.material3:material3:1.1.1
:androidx.compose.ui:ui:1.4.3
UIの概要
Surfaceは一枚のパネルを表現するUI要素です。
例はアプリの背景として利用されているSurfaceです。
:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
/* コンテンツ */
}
}
}
}
:
様々なコンポーネントのバックパネルとして利用される事が多いです。
関数の引数
Surfaceの状態(コンテンツ、装飾、演出など)は、引数により指定できます。
@Composable
@NonRestartableComposable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(color),
tonalElevation: Dp = 0.dp,
shadowElevation: Dp = 0.dp,
border: BorderStroke? = null,
content: @Composable () -> Unit
)
| 引数 | 概要 | |
|---|---|---|
| checked | Boolean | チェックの状態:true/false |
| onCheckedChange | (Boolean) -> Unit | チェックの変化で実行する関数オブジェクト |
| selected | Boolean | セレクトの状態:true/false |
| onClick | ( ) -> Unit | クリック、セレクトで実行する関数オブジェクト |
| enable | Boolean | UI操作の有効:true/無効:false ※操作:クリック、セレクト、チェック |
| modifier | Modifier | UI全般の装飾パラメータ |
| shape | Shape | 角の形状 |
| color | Color | コンポーネント(パネル)の色 |
| contentColor | Color | コンテンツの色 |
| tonalElevation | Dp | 色調を変える高さ(高:濃い~低:薄い) |
| shadowElevation | Dp | 影の長さを変える高さ(高:長い~低:短い) |
| border | BorderStroke | 輪郭 |
| interactionSource | MutableInteractionSource | ユーザが行ったUI操作の観測と報告 |
| content | ( ) -> Unit | 内部に配置するコンテンツ |
引数の組み合わせにより、4タイプのSurfaceが提供されています。違いはイベント処理関連の引数の有無です。
| Surface引数 | Type1 | Type2 | Type3 | Type4 | |
|---|---|---|---|---|---|
| checked | 〇 | ※1 | |||
| onCheckedChange | 〇 | ||||
| selected | 〇 | ||||
| onClick | 〇 | 〇 | |||
| enable | 〇 | 〇 | 〇 | ||
| interactionSource | 〇 | 〇 | 〇 | ||
| modifier | 〇 | 〇 | 〇 | 〇 | |
| shape | 〇 | 〇 | 〇 | 〇 | |
| color | 〇 | 〇 | 〇 | 〇 | |
| contentColor | 〇 | 〇 | 〇 | 〇 | |
| tonalElevation | 〇 | 〇 | 〇 | 〇 | |
| shadowElevation | 〇 | 〇 | 〇 | 〇 | |
| border | 〇 | 〇 | 〇 | 〇 | |
| content | 〇 | 〇 | 〇 | 〇 | |
| ※1:イベント処理関連 【用途】 Type1:アプリ画面の背景 Type2:Buttonの形状 【特徴】 Type2,3,4:リップルエフェクト有 |
|||||
関数の構成
SurfaceはComposable関数にBoxを使って、UIの機能を持たせた構成になっています。Boxはインライン関数なので展開されて階層は残りません。
CompositionLocalProviderはCompositionLocalで常用される関数です。これについては後述します。

コンポーネントとコンテンツの色
Surfaceを使うと、コンポーネント(パネル)に合わせたコンテンツの色が自動設定されたるようになっています。
カラーシステム
マテリアルデザインにカラーシステムという指針があります。
カラーシステムは色に役割(主役、脇役、など)を持たせています。さらに、役割毎にコンポーネントとコンテンツの色を定義しています。※詳細は「Color system」を参照

コンポーネントとコンテンツの色は対になる色です。
例えば、アプリの主画面に配置するButtonは、次のような配色になります。

このカラーシステムに従った配色はテーマの意図に基づいて決定し、ColorSchemeクラスに格納されます。そして、LocalColorSchemeというCompositionLocal変数で管理されています。
ColorSchemeに格納された色は次のように参照できます。
※MaterialTheme.colorSchemeはColorSchemeクラスのインスタンスを返す
val _primasy = MaterialTheme.colorScheme.primary
val _onPrimasy = MaterialTheme.colorScheme.onPrimary
val _secondary = MaterialTheme.colorScheme.secondary
val _tertiary = MaterialTheme.colorScheme.tertiary
val _background = MaterialTheme.colorScheme.background
val _error = MaterialTheme.colorScheme.error
val _surface = MaterialTheme.colorScheme.surface
:
:
色の自動設定
Surfaceは引数colorへコンポーネントの色を指定すると、初期値付き引数の動作により、引数contentColorへコンテンツの色が設定されるようになっています。
colorはそのままパネルの色として利用されます。
contentColorはLocalContentColorというCompositionLocal変数へ格納されて、Surface階下のUIツリーをスコープに持つ変数になります。これを行っているのがCompositionLocalProvider( )です。
※CompositionLocalについては「」を参照
@Composable
@NonRestartableComposable
fun Surface(
...
color: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(color), // 対になる色を返す
tonalElevation: Dp = 0.dp,
...
) {
val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation
CompositionLocalProvider( // スコープの指定
LocalContentColor provides contentColor,
LocalAbsoluteTonalElevation provides absoluteElevation
) {
Box(...) { // ↑
content() // CompositionLocal変数のスコープ範囲
} // ↓
}
}
例:Surface上のText
Surface上のTextを例にして、コンポーネントとコンテンツの色の扱われ方を示します。
Surfaceはcolorに指定された色(background)の対になる色(onBackground)をLocalContentColorへ設定します。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Text(text = "Hello World !!", fontSize = 32.sp)
}
}
}
}
}
Textは引数colorを指定しない場合、LocalContentColorから描画する色を取得します(ハイライト部分)。
@Composable
fun Text(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
...,
style: TextStyle = LocalTextStyle.current
) {
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current
}
}
// NOTE(text-perf-review): It might be worthwhile writing a bespoke merge implementation that
// will avoid reallocating if all of the options here are the defaults
val mergedStyle = style.merge(
TextStyle(
color = textColor,
...
)
)
BasicText(
text,
modifier,
mergedStyle,
onTextLayout,
overflow,
softWrap,
maxLines,
)
}
その結果、カラーシステムの指針に沿った配色の画面が出力されます。

関連記事:
