「Compose Compiler Reports」 recompose される条件とタイミングと範囲を知りたい

サンプル として、

Screen-level と その中に Button 3つの composable と ViewModel でカウンターを作ります。


@HiltViewModel
class HomeViewModel @Inject constructor() : ViewModel() {

  private val _countState = mutableStateOf(0)
  val countState: State<Int> = _countState

  private val _countStateFlow = MutableStateFlow(0)
  val countStateFlow = _countStateFlow.asStateFlow()

  fun incState() {
    _countState.value += 1
  }

  fun incStateFlow() {
    _countStateFlow.value += 1
  }

}


@Composable
fun HomeScreen(
  viewModel: HomeViewModel = hiltViewModel()
) {

  Timber.d("### HomeScreen composed.")

  var countA by remember { mutableStateOf(0) }
  val countB by viewModel.countState
  val countC by viewModel.countStateFlow.collectAsStateWithLifecycle()

  val onClickA = { countA += 1 }
  val onClickB = { viewModel.incState() }
  val onClickC = { viewModel.incStateFlow() }


  Text("${System.currentTimeMillis()}")

  ButtonA(countA, onClickA)

  Divider()

  ButtonB(countB, onClickB)

  Divider()

  ButtonC(countC, onClickC)

}

@Composable
private fun ButtonA(count: Int, onClick: () -> Unit) {

  Timber.d("### ButtonA(${count}, ${onClick.hashCode()}) composed.")

  Button(onClick = onClick) {
    Text("A: $count")
  }

}

@Composable
private fun ButtonB(count: Int, onClick: () -> Unit) {

  Timber.d("### ButtonB(${count}, ${onClick.hashCode()}) composed.")

  Button(onClick = onClick) {
    Text("B: $count")
  }

}

@Composable
private fun ButtonC(count: Int, onClick: () -> Unit) {

  Timber.d("### ButtonC(${count}, ${onClick.hashCode()}) composed.")

  Button(onClick = onClick) {
    Text("C: $count")
  }

}

ボタンを順番に押していきます。

挙動とログを予想できますか。

 

■ 結果

クリックのたびに、

Screen が recompose されています。

クリックしてないボタンも recompose されていますね!

Recompose Highlighter でもやってみました。



そうなるんですか。

なんでかな。

 

■ 「Compose Compiler Reports」 を使う

ツールを使って書き出してみます。

The Compose Compiler plugin can generate reports / metrics around certain compose-specific concepts that can be useful to understand what is happening with some of your compose code at a fine-grained level.

👉 androidx/compiler-metrics.md at androidx-main · androidx/androidx hatena-bookmark


// build.gradle

kotlinOptions {
  if (project.findProperty("composeCompilerReports") == "true") {
    freeCompilerArgs += [
        "-P",
        "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
            project.buildDir.absolutePath + "/compose_compiler"
    ]
  }
  if (project.findProperty("composeCompilerMetrics") == "true") {
    freeCompilerArgs += [
        "-P",
        "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
            project.buildDir.absolutePath + "/compose_compiler"
    ]
  }
}


./gradlew assembleRelease -PcomposeCompilerReports=true


./gradlew assembleRelease -PcomposeCompilerMetrics=true

👉 Jetpack Compose Stability Explained | by Ben Trengrove | Android Developers | Jul, 2022 | Medium hatena-bookmark

Compose Compiler Reports


// app_release-composables.txt

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HomeScreen(
  unstable viewModel: HomeViewModel? = @dynamic hiltViewModel(null, $composer, 0, 0b0001)
)

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ButtonA(
  stable count: Int
  stable onClick: Function0<Unit>
)

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ButtonB(
  stable count: Int
  stable onClick: Function0<Unit>
)

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ButtonC(
  stable count: Int
  stable onClick: Function0<Unit>
)

ViewModel が untable で @dynamic です。


// app_release-classes.txt

unstable class HomeViewModel {
  stable val _countState: MutableState<Int>
  stable val countState: State<Int>
  unstable val _countStateFlow: MutableStateFlow<Int>
  unstable val countStateFlow: StateFlow<Int>
  <runtime stability> = Unstable
}

Flow が unstable なので ViewModel として unstable となっているように見えます。


// app_release-composables.csv

name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
HomeScreen,1,1,1,0,0,0,1,0,2,2,
ButtonA,1,1,1,0,0,0,0,0,1,1,
ButtonB,1,1,1,0,0,0,0,0,1,1,
ButtonC,1,1,1,0,0,0,0,0,1,1,

Button たちは、すべて同じ数字。

Screen はそれらと違う。

なんとく、ViewModel が原因のような雰囲気。

しかし、気安く ViewModel に @Stable とかつけたくないし。

 

■ remember を使う

onClick* のラムダが毎回初期化されているというので、


// val onClickB = { viewModel.incState() }
// val onClickC = { viewModel.incStateFlow() }

val onClickB = remember { { viewModel.incState() } }
val onClickC = remember { { viewModel.incStateFlow() } }

とすべて remember 扱いにします。

イメージしていたログ出力になりました。

 

■ 「::」を使う

こんな記述はどう? というので書き換えてみます。


// val onClickB = { viewModel.incState() }
// val onClickC = { viewModel.incStateFlow() }

// val onClickB = remember { { viewModel.incState() } }
// val onClickC = remember { { viewModel.incStateFlow() } }

val onClickB = viewModel::incState
val onClickC = viewModel::incStateFlow

これもイメージしていたログ出力になりました。

少し驚きました。

 

■ State 取得部分はどうなの?

Screen 自体が recompose されてるのなら、こうしたほうがいいのでは?


// val countB by viewModel.countState
// val countC by viewModel.countStateFlow.collectAsStateWithLifecycle()

val countB by remember { viewModel.countState }
val countC by remember { viewModel.countStateFlow }.collectAsStateWithLifecycle()

...

もう、知らん...

(ここは後日検証します。)

 

■ まとめ

そもそも recompose の条件とタイミングをコード目視で把握できますかね。


「考えないほうがいい、きちんと行儀よくコードを書け」的な記事もよく見かけます。

しかし、それで SideEffect系 きちんと使える気がしません。

では、だれか decompose をお願いします。

👉 パフォーマンスに任せてコードの見通しを優先するほうが「recompose沼」にはまらなくていいのではという逆説。 - Qiita hatena-bookmark

👉 takahirom/decomposer: Gradle Plugin that allows you to decompile bytecode compiled with Jetpack Compose Compiler Plugin into Java and check it hatena-bookmark

👉 【Jetpack Compose】「Layout Inspector Recomposition counts」で re-compose 回数を確認する hatena-bookmark


【Jetpack Compose】Square 製 Radiography で View の構造を確認する

👉 square/radiography: Text-ray goggles for your Android UI. hatena-bookmark


dependencies {
  implementation 'androidx.compose.ui:ui-tooling:1.0.0-betaXY'
  implementation 'com.squareup.radiography:radiography:2.4.1'
}

scan() すると、画面のツリー構造がテキストで取得できるようでです。


DecorView { 1080×2160px }
├─LinearLayout { id:main, 1080×1962px }
│ ├─EditText { id:username, 580×124px, focused, text-length:0, ime-target }
│ ├─EditText { id:password, 580×124px, text-length:0 }
│ ╰─LinearLayout { 635×154px }
│   ├─Button { id:signin, 205×132px, text-length:7 }
│   ╰─Button { id:forgot_password, 430×132px, text-length:15 }
├─View { id:navigationBarBackground, 1080×132px }
╰─View { id:statusBarBackground, 1080×66px }


// Render the view hierarchy for all windows.
val prettyHierarchy = Radiography.scan()


// Extension function on View, renders starting from that view.
val prettyHierarchy = someView.scan()

 

Jetpack Compose でやってみる

この画面でやってみます。

square / radiography

Composable にコードを追加しておきます。


@Composable
fun HomeScreen(
  viewModel: HomeViewModel = hiltViewModel()
) {

  // ...

  val view: View = LocalView.current

  SideEffect {
    println(Radiography.scan())
    println(view.scan())
  }

}

👉 SideEffect - Compose における副作用  |  Jetpack Compose  |  Android Developers hatena-bookmark

 

結果


I: window-focus:false
I:  DecorView { 1080×2160px }
I:  ├─LinearLayout { 1080×2116px }
I:  │ ├─ViewStub { id:action_mode_bar_stub, GONE, 0×0px }
I:  │ ╰─FrameLayout { id:content, 1080×2039px }
I:  │   ╰─ComposeView { 1080×2039px }
I:  │     ╰─AndroidComposeView { 1080×2039px }
I:  │       ╰─CompositionLocalProvider { 1080×2039px }
I:  │         ╰─ScaffoldLayout { 1080×2039px }
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           │ ╰─SnackbarHost
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           ├─<subcomposition of ScaffoldLayout>
I:  │           │ ╰─CompositionLocalProvider { 1080×154px }
I:  │           │   ├─NavBottomBar { 1080×154px }
I:  │           │   │ ╰─Row { 1080×154px, SELECTABLE-GROUP }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 162×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 96×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 96×44px, text-length:4 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 151×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 85×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 85×44px, text-length:5 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 156×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ├─RenderVectorGroup
I:  │           │   │   │   │ │ ├─RenderVectorGroup
I:  │           │   │   │   │ │ ├─RenderVectorGroup
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 90×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 90×44px, text-length:5 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 150×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 84×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 84×44px, text-length:4 }
I:  │           │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │   │ ╰─BottomNavigationTransition { 146×154px }
I:  │           │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │   │   │ ├─Icon
I:  │           │   │   │   │ │ ╰─RenderVectorGroup
I:  │           │   │   │   │ ╰─Icon { 66×66px }
I:  │           │   │   │   ╰─Box { 80×44px, layout-id:"label" }
I:  │           │   │   │     ╰─ProvideTextStyle { 80×44px, text-length:4 }
I:  │           │   │   ╰─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:  │           │   │     ╰─BottomNavigationTransition { 180×154px }
I:  │           │   │       ├─Box { 66×66px, layout-id:"icon" }
I:  │           │   │       │ ├─Icon
I:  │           │   │       │ │ ╰─RenderVectorGroup
I:  │           │   │       │ ╰─Icon { 66×66px }
I:  │           │   │       ╰─Box { 114×44px, layout-id:"label" }
I:  │           │   │         ╰─ProvideTextStyle { 114×44px, text-length:8 }
I:  │           │   ╰─AdaptiveAd { 1080×0px }
I:  │           │     ╰─ViewFactoryHolder { 1080×0px }
I:  │           │       ╰─AdView { 1080×0px }
I:  │           │         ╰─FrameLayout { 0×0px }
I:  │           ╰─<subcomposition of ScaffoldLayout>
I:  │             ╰─NavHost
I:  │               ╰─Box
I:  │                 ╰─LocalOwnersProvider
I:  │                   ├─TopBar
I:  │                   │ ╰─CompositionLocalProvider
I:  │                   │   ├─Row
I:  │                   │   │ ╰─CompositionLocalProvider { roll:Button }
I:  │                   │   │   ├─CompositionLocalProvider
I:  │                   │   │   │ ╰─RenderVectorGroup
I:  │                   │   │   ╰─CompositionLocalProvider
I:  │                   │   ├─Row
I:  │                   │   │ ╰─ProvideTextStyle { text-length:4 }
I:  │                   │   ╰─CompositionLocalProvider
I:  │                   ╰─Column
I:  │                     ├─Button { roll:Button }
I:  │                     │ ╰─CompositionLocalProvider
I:  │                     │   ╰─Text { text-length:7 }
I:  │                     ├─Spacer
I:  │                     ├─Button { roll:Button }
I:  │                     │ ╰─CompositionLocalProvider
I:  │                     │   ╰─Text { text-length:7 }
I:  │                     ├─Spacer
I:  │                     ├─Button { roll:Button }
I:  │                     │ ╰─CompositionLocalProvider
I:  │                     │   ╰─Text { text-length:7 }
I:  │                     ├─Spacer
I:  │                     ╰─Button { roll:Button }
I:  │                       ╰─CompositionLocalProvider
I:  │                         ╰─Text { text-length:5 }
I:  ├─View { id:navigationBarBackground, 1080×44px }
I:  ╰─View { id:statusBarBackground, 1080×77px }

I: AndroidComposeView:
I: window-focus:false
I:  AndroidComposeView { 1080×2039px }
I:  ╰─CompositionLocalProvider { 1080×2039px }
I:    ╰─ScaffoldLayout { 1080×2039px }
I:      ├─<subcomposition of ScaffoldLayout>
I:      ├─<subcomposition of ScaffoldLayout>
I:      │ ╰─SnackbarHost
I:      ├─<subcomposition of ScaffoldLayout>
I:      ├─<subcomposition of ScaffoldLayout>
I:      │ ╰─CompositionLocalProvider { 1080×154px }
I:      │   ├─NavBottomBar { 1080×154px }
I:      │   │ ╰─Row { 1080×154px, SELECTABLE-GROUP }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 162×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 96×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 96×44px, text-length:4 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 151×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 85×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 85×44px, text-length:5 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 156×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ├─RenderVectorGroup
I:      │   │   │   │ │ ├─RenderVectorGroup
I:      │   │   │   │ │ ├─RenderVectorGroup
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 90×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 90×44px, text-length:5 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 150×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 84×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 84×44px, text-length:4 }
I:      │   │   ├─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │   │ ╰─BottomNavigationTransition { 146×154px }
I:      │   │   │   ├─Box { 66×66px, layout-id:"icon" }
I:      │   │   │   │ ├─Icon
I:      │   │   │   │ │ ╰─RenderVectorGroup
I:      │   │   │   │ ╰─Icon { 66×66px }
I:      │   │   │   ╰─Box { 80×44px, layout-id:"label" }
I:      │   │   │     ╰─ProvideTextStyle { 80×44px, text-length:4 }
I:      │   │   ╰─BottomNavigationItem { 180×154px, roll:Tab, SELECTED }
I:      │   │     ╰─BottomNavigationTransition { 180×154px }
I:      │   │       ├─Box { 66×66px, layout-id:"icon" }
I:      │   │       │ ├─Icon
I:      │   │       │ │ ╰─RenderVectorGroup
I:      │   │       │ ╰─Icon { 66×66px }
I:      │   │       ╰─Box { 114×44px, layout-id:"label" }
I:      │   │         ╰─ProvideTextStyle { 114×44px, text-length:8 }
I:      │   ╰─AdaptiveAd { 1080×0px }
I:      │     ╰─ViewFactoryHolder { 1080×0px }
I:      │       ╰─AdView { 1080×0px }
I:      │         ╰─FrameLayout { 0×0px }
I:      ╰─<subcomposition of ScaffoldLayout>
I:        ╰─NavHost
I:          ╰─Box
I:            ╰─LocalOwnersProvider
I:              ├─TopBar
I:              │ ╰─CompositionLocalProvider
I:              │   ├─Row
I:              │   │ ╰─CompositionLocalProvider { roll:Button }
I:              │   │   ├─CompositionLocalProvider
I:              │   │   │ ╰─RenderVectorGroup
I:              │   │   ╰─CompositionLocalProvider
I:              │   ├─Row
I:              │   │ ╰─ProvideTextStyle { text-length:4 }
I:              │   ╰─CompositionLocalProvider
I:              ╰─Column
I:                ├─Button { roll:Button }
I:                │ ╰─CompositionLocalProvider
I:                │   ╰─Text { text-length:7 }
I:                ├─Spacer
I:                ├─Button { roll:Button }
I:                │ ╰─CompositionLocalProvider
I:                │   ╰─Text { text-length:7 }
I:                ├─Spacer
I:                ├─Button { roll:Button }
I:                │ ╰─CompositionLocalProvider
I:                │   ╰─Text { text-length:7 }
I:                ├─Spacer
I:                ╰─Button { roll:Button }
I:                  ╰─CompositionLocalProvider
I:                    ╰─Text { text-length:5 }

どう使います?、これ。

scan() 時のオプションもいろいろあるようなので試してみますか。


【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認する【AndroidStudio】

ここ、なぜ出ないの?

【Jetpack Compose】テーマデフォルトの色を確認したい【AndroidStudio】

コードを追っていけば分かるが面倒。

特に、デフォルト色をひと目で分かるやつがほしい。

Light も Dark も、こういう感じの。

【Jetpack Compose】テーマデフォルトの色を確認したい【AndroidStudio】

👉 Dark theme - Material Design hatena-bookmark

いいのだが実際の色があれば分かりやすいのに、の Material3一覧 は以下。


👉 Color theming with MDC-Android – Material Design 3 hatena-bookmark

しかも、ブラウザをいちいち開けるのもなんかだるい。(開けてるけど。)

色の視覚的な記憶、って出来なくない?

いい方法ないか。

 

リンクをコメントで書いておく

⌘(command) キーとクリックでブラウザ開きますから。

Color theming with MDC-Android – Material Design 3

どうせ、ブラウザは開けてるはず。

 

デフォルト色の記述を変えて書いておく

AndroidStudio エディタの左にも表示されるような記述で、敢えて再度書く。

【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認したい【AndroidStudio】

Light と Dark の比較が欲しくなってきました。

 

まとめ

もう全部書いてやりました。

視覚的に分かりやすく、Light/Dark も比較できます。

【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認したい【AndroidStudio】

【Jetpack Compose】テーマデフォルトの色 (baseline palette) を確認したい【AndroidStudio】

以下、色味を確認するためだけのコード。


@Suppress("UNUSED_VARIABLE")
val default = listOf(

  // material3
  // light             dark
  Color(0xff6750a4), Color(0xffd0bcff), // colorPrimary
  Color(0xffffffff), Color(0xff381e72), // colorOnPrimary
  Color(0xffeaddff), Color(0xff4f378b), // colorPrimaryContainer
  Color(0xff21005d), Color(0xffeaddff), // colorOnPrimaryContainer
  Color(0xffd0bcff), Color(0xff6750a4), // colorPrimaryInverse
  Color(0xff625b71), Color(0xffccc2dc), // colorSecondary
  Color(0xffffffff), Color(0xff332d41), // colorOnSecondary
  Color(0xffe8def8), Color(0xff4a4458), // colorSecondaryContainer
  Color(0xff1d192b), Color(0xffe8def8), // colorOnSecondaryContainer
  Color(0xff7d5260), Color(0xffefb8c8), // colorTertiary
  Color(0xffffffff), Color(0xff492532), // colorOnTertiary
  Color(0xffffd8e4), Color(0xff633b48), // colorTertiaryContainer
  Color(0xff31111d), Color(0xffffd8e4), // colorOnTertiaryContainer
  Color(0xffb3261e), Color(0xfff2b8b5), // colorError
  Color(0xffffffff), Color(0xff601410), // colorOnError
  Color(0xfff9dedc), Color(0xff8c1d18), // colorErrorContainer
  Color(0xff410e0b), Color(0xfff2b8b5), // colorOnErrorContainer
  Color(0xff79747e), Color(0xff938f99), // colorOutline
  Color(0xfffffbfe), Color(0xff1c1b1f), // android:colorBackground
  Color(0xff1c1b1f), Color(0xffe6e1e5), // colorOnBackground
  Color(0xfffffbfe), Color(0xff1c1b1f), // colorSurface
  Color(0xff1c1b1f), Color(0xffe6e1e5), // colorOnSurface
  Color(0xffe7e0ec), Color(0xff49454f), // colorSurfaceVariant
  Color(0xff49454f), Color(0xffcac4d0), // colorOnSurfaceVariant
  Color(0xff313033), Color(0xffe6e1e5), // colorSurfaceInverse
  Color(0xfff4eff4), Color(0xff313033), // colorOnSurfaceInverse

  // material
  // light             dark
  Color(0xFF6200EE), Color(0xFFBB86FC), // primary
  Color(0xFF3700B3), Color(0xFF3700B3), // primaryVariant
  Color(0xFFFFFFFF), Color(0xFF000000), // onPrimary
  Color(0xFF03DAC6), Color(0xFF03DAC6), // secondary
  Color(0xFF018786), Color(0xFF03DAC6), // secondaryVariant
  Color(0xFF000000), Color(0xFF000000), // onSecondary
  Color(0xFFFFFFFF), Color(0xFF121212), // background
  Color(0xFF000000), Color(0xFFFFFFFF), // onBackground
  Color(0xFFFFFFFF), Color(0xFF121212), // surface
  Color(0xFF000000), Color(0xFFFFFFFF), // onSurface
  Color(0xFFB00020), Color(0xFFCF6679), // error
  Color(0xFFFFFFFF), Color(0xFF000000), // onError

  // Color.Any
  Color(0xFF000000), // Color.Black,
  Color(0xFF444444), // Color.DarkGray
  Color(0xFF888888), // Color.Gray
  Color(0xFFCCCCCC), // Color.LightGray
  Color(0xFFFFFFFF), // Color.White
  Color(0xFFFF0000), // Color.Red
  Color(0xFF00FF00), // Color.Green
  Color(0xFF0000FF), // Color.Blue
  Color(0xFFFFFF00), // Color.Yellow
  Color(0xFF00FFFF), // Color.Cyan
  Color(0xFFFF00FF), // Color.Magenta
  Color(0x00000000), // Color.Transparent

)

プラグインとかねえかな。

👉 IntelliJ IDEA プラグイン「Rainbow Brackets」を使う hatena-bookmark
👉 @SuppressWarnings て使っていますか。 hatena-bookmark