Compose提供了系统化的方法来帮助我们自定义主题,这让我们在实现暗黑主题以及其他颜色主题的时候非常非常的方便。
这节的需求很简单,我们的App需要有蓝,红,绿,黄四种主题色,在点击tab的时候分别切换不同的主题色,大致效果如下所示。
比如我们的工程名叫ComposeComing,那么工程建好后默认的Activity中会有如下代码:
setContent {
ComposeComingTheme {
Surface(color = MaterialTheme.colors.background) {
}
}
}
上面有两处代码很可疑:
首先这个 ComposeComingTheme() 函数我们点进去看下,你会发现该组合函数在 包名.ui.theme 文件夹下的 Theme.kt 文件中:
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200
)
@Composable
fun ComposeComingTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
通过上述代码,我们可以了解到:ComposeComingTheme 该组合函数在 MaterialTheme 上构建而成,MaterialTheme由colors、typography、shapes属性组成。然后该组合函数默认根据系统 是否是暗黑主题 将 颜色属性 进行了不同的定义。如果是暗黑主题使用DarkColorPalette,否则使用LightColorPalette。darkColors和lightColors都继承自Colors,并有相关的默认颜色值。所以我们的需求-自定义不同的主题颜色可以在这里进行突破了。
Colors中的所有可配的值如下所示:
@Stable
class Colors(
primary: Color,
primaryVariant: Color,
secondary: Color,
secondaryVariant: Color,
background: Color,
surface: Color,
error: Color,
onPrimary: Color,
onSecondary: Color,
onBackground: Color,
onSurface: Color,
onError: Color,
isLight: Boolean
)
而 MaterialTheme.colors.background 中最终调用的就是相关主题中设置的Colors的 background 属性值。
接下来我们根据需求进行主题的自定义,首先给大家点颜色看看,在ui.theme文件夹下的Color.kt文件中添加如下颜色:
val blue = Color(0xFF579DFD)
val red = Color(0xFFEB685E)
val green = Color(0xFF27BA6A)
val yellow = Color(0xFFFFCB1F)
然后在ui.theme文件夹下的Theme.kt文件中自定义主题,在这里我们统一使用了lightColors,然后根据传递进来的主题的名称将primary属性设置为了不同的颜色,如下所示:
@Composable
fun MyAppTheme(
themeName: String = "blue",
content: @Composable() () -> Unit
) {
val colors = when (themeName) {
"red" -> lightColors(primary = red)
"green" -> lightColors(primary = green)
"yellow" -> lightColors(primary = yellow)
else -> lightColors(primary = blue)
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
这样我们主题就已经定义好了,虽然只有primary属性会根据不同主题变化,但是这足够我们演示了。
接下来是TabRow的实现,我们将TabRow的背景颜色 backgroundColor 设置为刚定义的 primary 的颜色,点击不同tab的时候设置不同的主题,并将点击事件提升到上层处理,代码如下所示,:
@Composable
fun MyTabRow(
onTabItemClick: (name: String) -> Unit,
indexDefault: Int = 0
) {
val indexState = remember {
mutableStateOf(indexDefault)
}
val colorsTab = arrayOf("blue", "red", "green", "yellow")
TabRow(
selectedTabIndex = indexState.value,
modifier = Modifier
.fillMaxWidth()
.height(46.dp),
backgroundColor = MaterialTheme.colors.primary
) {
for ((index, name) in colorsTab.withIndex()) {
Tab(
selected = index == indexState.value,
onClick = {
indexState.value = index
onTabItemClick(colorsTab[index])
},
modifier = Modifier.fillMaxHeight(),
) {
Text(
text = name,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
textAlign = TextAlign.Center
)
}
}
}
}
先来预览下吧,我们在自定的主题MyAppTheme中组合MyTabRow,然后给定相关的主题颜色,这里默认预览红色和绿色的主题:
@Preview(showBackground = true)
@Composable
fun RedThemePreview() {
MyAppTheme(themeName = "red") {
MyTabRow(
onTabItemClick = {
},
indexDefault = 1
)
}
}
@Preview(showBackground = true)
@Composable
fun GreenThemePreview() {
MyAppTheme(themeName = "green") {
MyTabRow(
onTabItemClick = {
},
indexDefault = 2
)
}
}
预览图没有问题,接下来就是整体的实现了,反正就一点 – 数据驱动,视图自响应,在onCreate()函数中:
setContent {
val themeName = remember {
mutableStateOf("blue")
}
MyAppTheme(themeName = themeName.value) {
Column {
MyTabRow(onTabItemClick = {
themeName.value = it
})
}
}
}
这样,我们点击不同tab的时候,改变themeName的值,MyAppTheme会自动根据相应的值进行主题的切换,是不是就达到了如下效果呢:
上述需求实在是太简单了,但是实际开发中我们可能有很多种地方都定义了不同的颜色,而Colors中只提供了12个相关颜色的定义,不够用啊!!!
那怎么办呢?扩展,字面意义上的扩展,同时也是kotlin中的扩展。直接给Colors扩展相关的属性:
val Colors.myTabRowBackground: Color
@Composable get() = if (isLight) red else blue
但是这种方式明显有弊端,就是只能根据是否是暗黑主题进行颜色的配置,无法支持到我们上述的那么多种主题,而且也比较麻烦。
而且我查遍了全网也没有见到使用var来进行属性扩展的示例,难道是不支持么?扔物线的视频让我自己研究,其他培训机构的老师讲到这里直接卡壳了。我需要给这个扩展的属性进行赋值和取值的操作,求指点。