[Android]Jetpack Compose自定义主题

1.ColorScheme

ColorScheme 是 Jetpack Compose 中的一个类,用于定义一组颜色,这些颜色共同构成了应用程序的颜色主题。在 Material Design 系统中,颜色方案帮助确保应用程序具有一致的视觉外观,同时也支持色彩的可访问性和美观性。

(1).作用

  • 一致性ColorScheme 提供了一套标准化的颜色定义,这些颜色在整个应用的不同组件和界面之间使用,从而保持视觉一致性。

  • 美观性:通过精心选择的颜色组合,ColorScheme 能够提高应用的整体美观性。

  • 可访问性:通过为不同的视觉元素(如前景和背景)提供足够对比的颜色,ColorScheme 增强了内容的可读性,使应用更易于使用,尤其是视觉障碍用户。

  • 主题切换:支持明暗模式切换或其他主题变体时,ColorScheme 可以轻松调整以适应不同的环境需求。

(2).包含的颜色

ColorScheme提供了如下这些方案

class ColorScheme(
    /* 主色系 */
    primary: Color, // 主色,用于应用的大部分 UI 元素,如按钮、选中的选项卡等。
    onPrimary: Color, // 在主色上清晰显示的颜色,通常用于文本或图标。
    primaryContainer: Color, // 主色的容器色,用于需要主色变体的元素背景。
    onPrimaryContainer: Color, // 在主色容器上清晰显示的颜色,通常用于文本或图标。
    inversePrimary: Color, // 主色的反色,用于在对比背景上需要主色时。

    /* 次色系 */
    secondary: Color, // 次级色,用于补充主色或用作次要的 UI 元素。
    onSecondary: Color, // 在次级色上清晰显示的颜色,通常用于文本或图标。
    secondaryContainer: Color, // 次级色的容器色,用于需要次级色变体的元素背景。
    onSecondaryContainer: Color, // 在次级色容器上清晰显示的颜色,通常用于文本或图标。

    /* 第三色系 */
    tertiary: Color, // 第三色,用于需要注意或区分的 UI 元素。
    onTertiary: Color, // 在第三色上清晰显示的颜色,通常用于文本或图标。
    tertiaryContainer: Color, // 第三色的容器色,用于背景或填充色。
    onTertiaryContainer: Color, // 在第三色容器上清晰显示的颜色,通常用于文本或图标。

    /* 背景与表面色 */
    background: Color, // 背景色,用于页面或组件的背景。
    onBackground: Color, // 在背景色上清晰显示的颜色,通常用于文本或图标。
    surface: Color, // 表面色,用于卡片、菜单和其他元素的背景。
    onSurface: Color, // 在表面色上清晰显示的颜色,通常用于文本或图标。
    surfaceVariant: Color, // 表面色的变体,用于需要区分的表面元素。
    onSurfaceVariant: Color, // 在表面色变体上清晰显示的颜色。
    surfaceTint: Color, // 表面色的着色,通常用于表面元素的图标或小组件。
    inverseSurface: Color, // 表面色的反色,用于需要高对比度的背景。
    inverseOnSurface: Color, // 在反表面色上清晰显示的颜色。

    /* 错误处理色 */
    error: Color, // 错误色,用于指示错误或警告状态,如输入校验失败。
    onError: Color, // 在错误色上清晰显示的颜色,通常用于错误文本或图标。
    errorContainer: Color, // 错误色的容器色,用于错误状态的背景。
    onErrorContainer: Color, // 在错误容器色上清晰显示的颜色。

    /* 其他 */
    outline: Color, // 用于元素边框的颜色。
    outlineVariant: Color, // 边框颜色的变体,可能用于更细微的分界线。
    scrim: Color, // 遮罩层颜色,通常用于遮盖或暗化背景中的内容。
) {
    ...
}

(3).使用示例

在 Jetpack Compose 中,你可以使用 MaterialTheme 来应用 ColorScheme,如下所示:

@Composable
fun MyAppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
    val colors = if (darkTheme) {
        darkColorScheme(
            primary = Color.Blue,
            secondary = Color.Green,
            background = Color.Black
        )
    } else {
        lightColorScheme(
            primary = Color.Blue,
            secondary = Color.Green,
            background = Color.White
        )
    }

    MaterialTheme(
        colorScheme = colors,
        typography = Typography,
        content = content
    )
}

2.颜色管理(ui.theme/Color.kt)

在Color.kt管理颜色

package com.randomdt.www.ui.theme

import androidx.compose.ui.graphics.Color
// 主题色 
val theme_color = Color(0xFF9279F8)
// 主要背景色
val theme_background_color = Color(0xFF161517)

// 文字主色 
val text_color = Color(0xFFFFFFFF)
// 文字辅色
val text_aux33 = Color(0xFF333333)
val text_aux66 = Color(0xFF666666)
val text_aux99 = Color(0xFF999999)

也可以想以前一样, 在colors.xml中管理颜色

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 主题色 -->
    <color name="theme_color">#9279F8</color>
    <!-- 主要背景色 -->
    <color name="theme_background_color">#161517</color>

    <!-- 文字主色 -->
    <color name="text_color">#FFFFFF</color>
    <!-- 文字辅色 -->
    <color name="text_aux33">#333333</color>
    <color name="text_aux66">#666666</color>
    <color name="text_aux99">#999999</color>

</resources>
@Composable
fun darkCustomTheme() = CustomThemeData (
    textColor = colorResource(id = R.color.text_aux33)
)

3.重设深色/浅色模式的主题颜色(ui.theme/Theme.kt)

如果需要更改ColorScheme对应的颜色值, 只需要在定义主题中对具体颜色重写赋值即可.

@Composable
fun reDarkColorScheme(): ColorScheme = darkColorScheme(
    primary = theme_color,
    background = theme_background_color
)

@Composable
fun reLightColorScheme(): ColorScheme = lightColorScheme(
    primary = theme_color,
    background = theme_background_color
)


@Composable
fun RandomdtTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = false,  // 禁用动态颜色, 这样无论设备运行的Android版本如何,都会使用你定义的颜色方案. 启用了动态颜色时, 会在 Android 12+ 上覆盖自定义颜色方案。
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> reDarkColorScheme()
        else -> reLightColorScheme()
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = content()
    )
    
}

使用示例:

组件中的获取颜色从,使用MaterialTheme.colorScheme.xxx这种方式.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            RandomdtTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                    // color = theme_background_color // 也可以直接从颜色管理获取颜色
                    // color = colorResource(id = R.color.theme_background_color) // 以前从Color.xml获取颜色
                ) {
                    Greeting("Android")
                }
            }
        }
    }
}

3.自定义主题

ColorScheme中的颜色名称是固定的, 如果你想在其中添加一个名称为“textColor”的颜色, 就办不到. 

这里, 我们定义成使用MaterialTheme.customScheme.xxx的方式获取属性. 

data class CustomThemeData(
    // 定义颜色
    val textColor: androidx.compose.ui.graphics.Color,
    // 定义形状
    // 定义字体
    ...
)

@Composable
fun darkCustomTheme() = CustomThemeData (
    textColor = text_aux33
)
@Composable
fun lightCustomTheme() = CustomThemeData (
    textColor = text_aux99
)
val CustomThemes = compositionLocalOf<CustomThemeData> { error("No Color provided") }

val MaterialTheme.customScheme: CustomThemeData
    @Composable
    @ReadOnlyComposable
    get() = CustomThemes.current


@Composable
fun RandomdtTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = false,  
    content: @Composable () -> Unit
) {
    ......

    CompositionLocalProvider(
        CustomThemes provides if (darkTheme) darkCustomTheme() else lightCustomTheme()
    ) {
        MaterialTheme(
            colorScheme = colorScheme,
            typography = Typography,
            content = content()
        )
    }
}

使用示例:

MyTheme {
    Text(
        text = "Hello Android!",
        color = MaterialTheme.customScheme.textColor,
    )
)

4.完整代码示例

package com.randomdt.www.ui.theme

import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
import com.randomdt.www.support.tools.findActivity

/** 修改主题中颜色 */
@Composable
fun reDarkColorScheme(): ColorScheme = darkColorScheme(
    primary = theme_color,
    background = theme_background_color
)

@Composable
fun reLightColorScheme(): ColorScheme = lightColorScheme(
    primary = theme_color,
    background = theme_background_color
)


/** 自定义主题 */
data class CustomThemeData(
    // 定义颜色
    val textColor: androidx.compose.ui.graphics.Color,
    // 定义形状
    // 定义字体

    var tag: Int = 10
)

@Composable
fun darkCustomTheme() = CustomThemeData (
    textColor = text_aux33
)
@Composable
fun lightCustomTheme() = CustomThemeData (
    textColor = text_aux99
)
val CustomThemes = compositionLocalOf<CustomThemeData> { error("No Color provided") }

val MaterialTheme.customScheme: CustomThemeData
    @Composable
    @ReadOnlyComposable
    get() = CustomThemes.current


/** 主题 */
@Composable
fun RandomdtTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = false,  // 禁用动态颜色, 这样无论设备运行的Android版本如何,都会使用你定义的颜色方案. 启用了动态颜色时, 会在 Android 12+ 上覆盖自定义颜色方案。
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> reDarkColorScheme()
        else -> reLightColorScheme()
    }

    CompositionLocalProvider(
        CustomThemes provides if (darkTheme) darkCustomTheme() else lightCustomTheme()
    ) {
        MaterialTheme(
            colorScheme = colorScheme,
            typography = Typography,
            content = {
                // 使用MaterialTheme.colorScheme获取颜色, 必须放在MaterialTheme之内.
                val backgroundColor = MaterialTheme.colorScheme.background
                val activity = LocalContext.current.findActivity()
                val view = LocalView.current
                val window = (view.context as Activity).window
                // 设置状态栏
                SideEffect {
                    activity?.window?.statusBarColor = backgroundColor.toArgb()
                    WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
                }

                content()
            }
        )
    }
}
import android.app.Activity
import android.content.Context

// 为 Context 类型定义的一个扩展函数
// 主要目的是从当前的 Context 对象中递归地寻找并返回一个 Activity 实例(如果存在的话)。这在 Android 开发中是很有用的,尤其是在需要在某些并非直接与 Activity 相关的代码(如工具类或扩展函数中)中访问 Activity 相关的功能时。
fun Context.findActivity(): Activity? = when (this) {
    is Activity -> this
    is android.content.ContextWrapper -> baseContext.findActivity()
    else -> null
}

相关推荐

  1. ElementUI定义主题

    2024-04-27 03:34:02       41 阅读
  2. [Android]Jetpack Compose定义主题

    2024-04-27 03:34:02       24 阅读
  3. 7、Copmose定义颜色和主题切换

    2024-04-27 03:34:02       44 阅读
  4. vscode 定义(修改已有)主题教程

    2024-04-27 03:34:02       34 阅读
  5. 主题切换之根元素CSS定义

    2024-04-27 03:34:02       31 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-27 03:34:02       91 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-27 03:34:02       97 阅读
  3. 在Django里面运行非项目文件

    2024-04-27 03:34:02       78 阅读
  4. Python语言-面向对象

    2024-04-27 03:34:02       88 阅读

热门阅读

  1. STM32 JTAG

    2024-04-27 03:34:02       36 阅读
  2. 好用的项目管理系统推荐,项目人必看!

    2024-04-27 03:34:02       27 阅读
  3. 鸿蒙小案例-搜索高亮

    2024-04-27 03:34:02       28 阅读
  4. MongoDB聚合运算符:$replaceOne

    2024-04-27 03:34:02       31 阅读
  5. Mybatis之if标签判断boolean值

    2024-04-27 03:34:02       33 阅读
  6. look-behind requires fixed-width pattern_正则表达式

    2024-04-27 03:34:02       30 阅读
  7. C++ Primer Plus

    2024-04-27 03:34:02       33 阅读
  8. manim

    2024-04-27 03:34:02       35 阅读
  9. Mysql索引篇

    2024-04-27 03:34:02       29 阅读
  10. 什么是prettier的glob 模式

    2024-04-27 03:34:02       33 阅读
  11. 【DataGrip】 sql语句:模糊搜索

    2024-04-27 03:34:02       35 阅读