Material3のTopAppBarは、コンテンツのスクロールに合わせて色が変化します。今回はこれの実現方法を紹介します。さらに、StatusBarの色もTopAppBarに合わせて変化させる方法も説明します。
※以前このページではスクロールに合わせてStatus Barの色を変化させる方法を紹介していましたが、material3のAPIの変更により、その方法が使えなくなりました。そのため、透明にしたStatus Barの裏側にTopAppBarを回り込ませる方法に変更しました。
目次
やりたいこと
Material3のTopAppBar(画面のタイトルなどが表示される部分。従来はToolBarやActionBarとも呼ばれていた。)は、コンテンツのスクロールに合わせて色を変化させるとガイドラインに定められています。一方でStatusBar(通知や時計などが表示される部分)の色はガイドラインには明記されていませんが、GmailなどのGoogle製アプリやAndroid標準の設定アプリなどでは、TopAppBarの色に合わせてStatusBarの色も変化しています。今回はこれを、TopAppBarScrollBehavior
とAccompanistのSystem UI Controllerを使って実現します。
サンプル
ソースコードはGitHubにあります。
主な環境は以下の通りです。
- Kotlin 1.7210
- Compose Compiler 1.3.2
- Compose Libraries 1.2.1
- Material3 1.0.0-rc01
- Accompanist 0.25.1
ソースコードと結果
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StatusBarColorOnScrollSample() {
val systemUiController = rememberSystemUiController()
val isDark = isSystemInDarkTheme()
SideEffect {
systemUiController.setStatusBarColor(
color = Color.Transparent,
darkIcons = !isDark,
)
}
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(topAppBarState)
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
CenterAlignedTopAppBar(
title = { Text("TopAppBar") },
scrollBehavior = scrollBehavior,
)
}
) { paddingValues ->
LazyColumn(contentPadding = paddingValues) {
items(100) { count ->
Text(
text = "Item ${count + 1}",
modifier = Modifier.fillMaxWidth().height(30.dp).padding(20.dp, 4.dp)
)
}
}
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/* StatusBarやNavigationBarの裏側にも描画する */
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
...
}
}
}
結果
説明
サンプルのようなStatusBarとTopAppBarの色の変化は、以下の3ステップで実現できます。
- TopAppBarの色をスクロールに合わせて変化させる
- StatusBarを透明にする
- TopAppBarをStatusBarの下に回り込ませる
依存関係の追加
dependencies {
implementation 'androidx.compose.material3:material3:x.x.x'
implementation 'com.google.accompanist:accompanist-systemuicontroller:x.x.x'
}
Material3に移行していることが前提になります。build.gradle
のdependencies
には、material3
ライブラリを追加します。Material3への移行について、詳しくは「Jetpack ComposeアプリをMaterial Design 3へ移行する」を参照してください。
また、AccompanistのSystem UI Controllerを使います。accompanist-systemuicontroller
を追加しておきます。System UI Controllerを使ってStatus Barの色を変化させる方法は、「Jetpack ComposeでStatus Barの色を変更したい」にも詳しく書いていますので、そちらも参考にしてください。
TopAppBarの色の変化
val topAppBarState = rememberTopAppBarState()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(topAppBarState)
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
CenterAlignedTopAppBar(
title = { Text("TopAppBar") },
scrollBehavior = scrollBehavior,
)
}
) { paddingValues ->
LazyColumn( ... ) { ... }
}
スクロールに合わせてTopAppBarの色を変化させるには、androidx.compose.material3
パッケージのTopAppBarScrollBehavior
を使います。
TopAppBarScrollBehavior
には、コンテンツをスクロールした時のTopAppBarの振る舞いが実装されていて、TopAppBarDefaults
に3種類(pinnedScrollBehavior
, enterAlwaysScrollBehavior
, exitUntilCollapsedScrollBehavior
)用意されています。最も基本的なpinnedScrollBehavior
は、TopAppBarの位置やサイズは固定で、色のみ変化させます。
TopAppBarScrollBehavior
オブジェクトを取得するには、TopAppBarState
オブジェクトが必要です。rememberTopAppBarState()
で取得したtopAppBarState
をpinnedScrollBehavior()
に渡して、scrollBehavior
を取得します。
TopAppBar
とスクロールコンテンツ(上の例ではLazyColumn
)は、Scaffold
を使ってレイアウトします。作成したscrollBehavior
オブジェクトをScaffold
内のTopAppBar
のscrollBehavior
にセットし、Scaffold
自体のmodifier
にscrollBehavior.nestedScrollConnection
を渡すことによって、コンテンツのスクロール状態とTopAppBarの状態が同期します。
StatusBarを透明にする
val systemUiController = rememberSystemUiController()
val isDark = isSystemInDarkTheme()
SideEffect {
systemUiController.setStatusBarColor(
color = Color.Transparent,
darkIcons = !isDark,
)
}
StatusBarの色は、AccompanistのSystem UI ControllerのsetStatusBarColor()
を使います。ここでは透明にしたいので、color
引数にColor.Transparent
を指定しています。
darkIcons
引数は、StatusBarのアイコンの色を黒にするかどうかを指定する引数です。isSystemInDarkMode
で現在ダークモードを使用中かどうかを取得し、ダークモード中はfalse
を指定して白いアイコンに、ライトモード中はtrue
を指定して黒いアイコンに設定しています。
StatusBarの後ろにTopAppBarを回り込ませる
WindowCompat.setDecorFitsSystemWindows(window, false)
StatusBarの領域にアプリのコンテンツを描画するには、WindowCompat.setDecorFitsSystemWindows
にfalse
を指定します。これは基本的にはActivityのonCreate
でsetContent
を呼び出す前に設定します。
この設定はデフォルトではtrue
になっており、この状態だとSystemBar (StatusBar + NavigationBar) の領域にはアプリのコンテンツが描画されません。そのままだと、TopAppBarがStatusBarの後ろに回り込んでくれないので、スクロールによってTopAppBarの色が変化してもStatusBarの部分は白いまま(ライトモードの場合)になってしまいます。
WindowCompat.setDecorFitsSystemWindows
にfalse
を指定することにより、TopAppBarがStatusBarの領域まで広がり、StatusBarの部分もスクロールに合わせて色が変化するようになります。
なお、WindowCompat.setDecorFitsSystemWindows
にfalse
を指定した場合、コンテンツ(文字やアイコンなど)がStatusBarやNavigationBarに重ならないように、Paddingを適切に設定する必要があるのですが、material3のTopAppBarはライブラリ側でPaddingを設定してくれているので、アプリ側では特に何もする必要がありません。(material 3 v1.0.0-beta01以降)
コンテンツは随時追加していきます。