Android JetPack Compose之主题的理解与使用

    }
}

@Composable
fun MessageCard(msg:Message){
   Row (modifier = Modifier.padding(all=8.dp).background(MaterialTheme.colors.background)){
       Image(painter = painterResource(id = R.drawable.portrait),
           contentDescription = null,
           modifier = Modifier
               .size(40.dp)
               .clip(CircleShape)

       )
       Column {
           Text(text = msg.author)
           Spacer(modifier = Modifier.height(4.dp))
           Text(text = msg.body)
       }
   }
}

data class Message(val author:String,val body:String)

}


接下来我们可以看下生成的HelloComposeTheme做了哪些事情,先看下主题的定义,如下所示:



@Composable
fun HelloComposeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}

MaterialTheme(
    colors = colors, // 颜色
    typography = Typography, // 字体
    shapes = Shapes, // 形状
    content = content // 视图
)

}

private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200,
background = Color.Gray
)

private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200,
background = Color.White
)


从上面代码中我们可以看出,Android Studio默认生成了两种调色板,DarkColorPalette 和 LightColorPalette ,他们分别对应深色主题和亮色主题,根据传入的布尔值参数选择不同的调色板,然后选择的调色板会被透传到MaterialTheme


然后我们继续看darkColors和lightColors两个方法,如下:



fun lightColors(
primary: Color = Color(0xFF6200EE),
primaryVariant: Color = Color(0xFF3700B3),
secondary: Color = Color(0xFF03DAC6),
secondaryVariant: Color = Color(0xFF018786),
background: Color = Color.White,
surface: Color = Color.White,
error: Color = Color(0xFFB00020),
onPrimary: Color = Color.White,
onSecondary: Color = Color.Black,
onBackground: Color = Color.Black,
onSurface: Color = Color.Black,
onError: Color = Color.White
): Colors = Colors(
primary,
primaryVariant,
secondary,
secondaryVariant,
background,
surface,
error,
onPrimary,
onSecondary,
onBackground,
onSurface,
onError,
true
)


从上面代码中可以看到,lightColors将所有传入的参数全部透传至Colors构造器中,,lightColors帮助我们生成了许多默认的属性值,其中的primary,secondary实际上都是Material Design设计规则规定的主题配色字段。我们可以看出两种调色板的本质只是主题的配色字段不同,我们也可以修改这些配色来满足我们的需求,具体的配色说明如下所示:




| Color定义 | 说明 |
| --- | --- |
| primary | 整个应用中最常用的主色 |
| primaryVariant | 主色的变种色,主要用于与主色调做区分的场景。例如APP Bar使用主色,系统状态栏就用变种色 |
| secondary | 次选色提供了一种用于强调和区分主色的能力,常常用于悬浮按钮,复选框,单选按钮,需要突出的文本,以及链接标题等场景 |
| secondaryVariant | 次选色的变种,用于与次选色做区分 |
| background | 背景色,目前主要用作Scaffold系列组件的背景色 |
| surface | 平面色,常用于平面组件的背景色,例如Surface组件,sheet组件与Menu组件等 |
| error | 错误色,常用于组件中表示错误的颜色 |
| onPrimary | 常用于使用primary作为背景色的组件之上的文本与icon的颜色 |
| onSecondary | 常用于secondary作为背景色的组件之上的文本与icon的颜色 |
| onBackground | 常用于使用background作为背景色的组件之上的文本与icon颜色 |
| onSurface | 常用于使用surface作为组件背景色的组件之上的文本与icon的颜色 |
| onError | 常用于使用error作为背景色的组件之上的文本与icon颜色 |


下面以一个例子结束本小节



> 
> 我们要展示一段文字,亮色主题下,字体颜色为红色,暗色主题下字体颜色为蓝色,如下图所示:
> 
> 
> 


亮色主题:


![](https://img-blog.csdnimg.cn/img_convert/8575efbefe619cb2194a6552b44404d2.webp?x-oss-process=image/format,png)


暗色主题:


![](https://img-blog.csdnimg.cn/img_convert/42c53c1b22e7fd0a2b838007d03ef2b3.webp?x-oss-process=image/format,png)


我们实现这个需求很简单,代码如下所示: 先在生成的Theme.kt中定义一个主题:



@Composable
fun CustomColorTheme(
isDark: Boolean,
content: @Composable() () -> Unit
) {
val BLUE = Color(0xFF0000FF)
val RED = Color(0xFFDC143C)
val colors = if (isDark) {
darkColors(primary = BLUE)
} else {
lightColors(primary = RED)
}

MaterialTheme(
    colors = colors,
    typography = Typography,
    shapes = Shapes,
    content = content
)

}


然后使用的时候添加上我们自定义的主题就行了,代码如下所示:



class TestCustomTheme : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CustomColorTheme(isSystemInDarkTheme()) {
// A surface container using the ‘background’ color from the theme
SampleText(“Android”)
}
}
}
}

@Composable
fun SampleText(name: String) {
Text(
text = “Hello $name!”,
style = MaterialTheme.typography.h4,
color = MaterialTheme.colors.primary
)
}


## 2.MaterialTheme与CompositionLocal的联系


### 2.1 MaterialTheme的工作原理


接下来,我们通过源码了解下MaterialTheme的工作原理,进入源码如下:



@Composable
fun MaterialTheme(
colors: Colors = MaterialTheme.colors,
typography: Typography = MaterialTheme.typography,
shapes: Shapes = MaterialTheme.shapes,
content: @Composable () -> Unit
) {
val rememberedColors = remember {
// Explicitly creating a new object here so we don’t mutate the initial [colors]
// provided, and overwrite the values set in it.
colors.copy()
}.apply { updateColorsFrom(colors) }
val rippleIndication = rememberRipple()
val selectionColors = rememberTextSelectionColors(rememberedColors)
CompositionLocalProvider(
LocalColors provides rememberedColors,
LocalContentAlpha provides ContentAlpha.high,
LocalIndication provides rippleIndication,
LocalRippleTheme provides MaterialRippleTheme,
LocalShapes provides shapes,
LocalTextSelectionColors provides selectionColors,
LocalTypography provides typography
) {
ProvideTextStyle(value = typography.body1) {
PlatformMaterialTheme(content)
}
}
}


从上面的代码中我们可以发现,MaterialTheme本身就是一个Composable组件,我们传入的content参数就是声明在Theme中的自定义视图页面组件,透传进ProvideTextStyle然后在其内部进行调用。其中使用了CompositionLocalProvider函数,通过prividers将rememberedColors提供给了LocalColors。


接下来我们继续看下MaterialTheme是如何获取到当前的主题配色的,我们使用的是`MaterialTheme.colors.primary`。这里可能有人会疑惑,MaterialTheme是一个Composable函数,为啥还能访问其成员属性呢?其实MaterialTheme还有一个同名的单例对象,如下所示:



/**

  • Contains functions to access the current theme values provided at the call site’s position in

  • the hierarchy.
    /
    object MaterialTheme {
    /
    *

    • Retrieves the current [Colors] at the call site’s position in the hierarchy.
    • @sample androidx.compose.material.samples.ThemeColorSample
      */
      val colors: Colors
      @Composable
      @ReadOnlyComposable
      get() = LocalColors.current

    /**

    • Retrieves the current [Typography] at the call site’s position in the hierarchy.
    • @sample androidx.compose.material.samples.ThemeTextStyleSample
      */
      val typography: Typography
      @Composable
      @ReadOnlyComposable
      get() = LocalTypography.current

    /**

    • Retrieves the current [Shapes] at the call site’s position in the hierarchy.
      */
      val shapes: Shapes
      @Composable
      @ReadOnlyComposable
      get() = LocalShapes.current
      }

从上面代码可以看出,当使用MaterialTheme单例对象去获取colors属性时,间接使用的是LocalColors.,这个LocalColors的定义如下:



internal val LocalColors = staticCompositionLocalOf { lightColors() }


通过定义可以知道,它是一个CompositionLocal,初始值是lightColor()返回的Colors配置,。MaterialTheme方法中通过CompoisitionLocalProvider方法为Composable提供了一些CompositionLocal,这其中就包含了所有的主题配置信息。


### 2.2 CompositionLocal


在很多时候我们需要在Composable视图树中共享一些数据,例如主题配置,一种有效的方式就是通过显示参数传递的方式实现,当参数越来越多的时候,Composable参数列表会变得越来越大并且越来越臃肿,很难维护。当Compose需要彼此间传递数据,并且实现各自的私有性时,如果仍然采用显示参数的方式,则可能会导致意外的崩溃和不可预料的麻烦,为了解决这个问题,Compose提出了CompositionLocal用来完成Composable树中共享数据。CompositionLocal是具有层级的,可以被限定在以某个Composable作为根节点的子树中,默认会向下传递,当然当前子树中的某个Composable可以对该CompositionLocal进行覆盖,从而使得新值会在这个Composable中继续向下传递。


`需要注意的是:我们只能在Composable中获取CompositionLocal保存的数据`



> 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值