一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天 点击查看活动详情。
Compose中的主题
Compose中的主题同样是使用代码来控制的
1. 主题设置
打开我们之前创建的Compose项目:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyComposeDemoTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Greeting("Android")
}
}
}
}
}
代码中使用了主题MyComposeDemoTheme,这个名字是在创建项目的时候根据项目名称自动生成的。下面我们看一下这个MyComposeDemoTheme中都有什么:
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200
/* Other default colors to override
background = Color.White,
surface = Color.White,
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
*/
)
@Composable
fun MyComposeDemoTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
很好理解,这里首先定义了两个Colors,分别是深色模式和浅色模式的主题默认颜色。下面的MyComposeDemoTheme是一个可组合函数,它接收两个参数,一个是是否为深色模式,一个是我们的布局。根据是否是深色模式来选择需要的Colors,然后将选好的Colors设置到MaterialTheme中。
我们点击去看看MaterialTheme都有什么?
@Composable
fun MaterialTheme(
colors: Colors = MaterialTheme.colors,
typography: Typography = MaterialTheme.typography,
shapes: Shapes = MaterialTheme.shapes,
content: @Composable () -> Unit
) {
...// 里面的具体实现我们就不看了
}
我们看到MaterialTheme也是一个组合函数,它有4个参数,除了已知的两个参数(colors、content),还有typography和shapes,typography是一组文本样式,shapes是组件要使用的一组形状。
2. 颜色设置
通过上面的主题,我们可以看到,里面的颜色是用的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
){
...
var primary by mutableStateOf(primary, structuralEqualityPolicy())
internal set
var primaryVariant by mutableStateOf(primaryVariant, structuralEqualityPolicy())
internal set
var secondary by mutableStateOf(secondary, structuralEqualityPolicy())
internal set
...
}
我们传的颜色通过构造方法传入了Colors中。我们看一下Colors类里面的代码,mutableStateOf我们看到了一个熟悉的单词,实在Compose状态时提到过的,这里将Color转为Compose可观察的State。
下面我们在来看看Color:
inline class Color(val value: ULong) {
@Stable
val colorSpace: ColorSpace
get() = ColorSpaces.getColorSpace((value and 0x3fUL).toInt())
fun convert(colorSpace: ColorSpace): Color
@Stable
val red: Float
@Stable
val green: Float
@Stable
val blue: Float
@Stable
val alpha: Float
companion object {
@Stable
val Black = Color(0xFF000000)
@Stable
val DarkGray = Color(0xFF444444)
@Stable
val Gray = Color(0xFF888888)
@Stable
val LightGray = Color(0xFFCCCCCC)
@Stable
val White = Color(0xFFFFFFFF)
@Stable
val Red = Color(0xFFFF0000)
@Stable
val Green = Color(0xFF00FF00)
@Stable
val Blue = Color(0xFF0000FF)
@Stable
val Yellow = Color(0xFFFFFF00)
@Stable
val Cyan = Color(0xFF00FFFF)
@Stable
val Magenta = Color(0xFFFF00FF)
@Stable
val Transparent = Color(0x00000000)
@Stable
val Unspecified = Color(0f, 0f, 0f, 0f, ColorSpaces.Unspecified)
}
}
代码进行了删减。可以看到,Color类就是一个数据存放类,用于存放颜色值的。
我们要如何自己设置颜色呢?下面是自定义颜色的方法:
// 通过ARGB来设置颜色
@Stable
fun Color(
red: Float,
green: Float,
blue: Float,
alpha: Float = 1f,
colorSpace: ColorSpace = ColorSpaces.Srgb
): Color {
...
}
@Stable
fun Color(/*@ColorInt*/ color: Int): Color {
return Color(value = color.toULong() shl 32)
}
@Stable
fun Color(color: Long): Color {
return Color(value = (color.toULong() and 0xffffffffUL) shl 32)
}
设置主题时,要注意需要给哪些属性设定颜色,Colors中都有默认值,下面记录一下Colors的每个参数对应的颜色分别是什么:
class Colors(
primary: Color,// 应用程序主要颜色
primaryVariant: Color, // 主要变体颜色,用于区分使用主要颜色的应用程序的两个元素
// 例如顶部应用程序栏和系统栏
secondary: Color, // 辅助颜色,用于浮动操作按钮、选择控件、复选框和单选按钮等
secondaryVariant: Color, // 辅助变体颜色,用于区分使用辅助颜色的应用的两个元素
background: Color, // 背景颜色
surface: Color, // 表面颜色,用于组件的表面,例如Card、menu
error: Color, // 错误颜色,用于指示组件(例如文本字段)中的错误
onPrimary: Color, // 用于显示在原色顶部的文本和图标的颜色
onSecondary: Color, // 用于显示在辅助颜色顶部的文本和图标的颜色
onBackground: Color, // 用于显示在北京颜色顶部的文本和图标的颜色
onSurface: Color, // 用于显示在表面颜色顶部的文本和图标的颜色
onError: Color, // 用于显示在错误颜色顶部的问题和图标的颜色
isLight: Boolean // 是否为浅色模式
)
有了这些注释之后,是不是就感觉很清晰了。 颜色的使用:
Text(text = "Hello $name!", color = MaterialTheme.colors.primary)
3. 字体设置
我们在设置主题的时候有提到过MaterialTheme中的一个参数:typography。Material定义了一个字体系统,可以使用一些常用的样式:比如字体样式、字体宽度、字号大小等等。那我们要在哪里设置字体呢?
记得我们在说到Compose项目目录的时候,在ui.theme包下面有一个Type.kt文件,字体的配置都可以写在这个文件中:
// Set of Material typography styles to start with
val Typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
/* Other default text styles to override
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
),
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
)
*/
)
接下来我们看看Typography中都有什么吧?
@Immutable
class Typography internal constructor(
val h1: TextStyle,
val h2: TextStyle,
val h3: TextStyle,
val h4: TextStyle,
val h5: TextStyle,
val h6: TextStyle,
val subtitle1: TextStyle,
val subtitle2: TextStyle,
val body1: TextStyle,
val body2: TextStyle,
val button: TextStyle,
val caption: TextStyle,
val overline: TextStyle
)
可以看到,这里面提到了很多字体,我们在Type.kt文件中就重写了body1、button和caption的字体。我们看到他们的返回类型是TextStyle,但是TextStyle中又有什么呢?
@Immutable
class TextStyle(
val color: Color = Color.Unspecified, // 颜色
val fontSize: TextUnit = TextUnit.Unspecified, // 字号大小
val fontWeight: FontWeight? = null, // 字体粗细
val fontStyle: FontStyle? = null, // 字体样式,比如斜体
val fontSynthesis: FontSynthesis? = null, // 在提供的自定义字体系列中找不到所需要的粗细或样式时,是否合成字体粗细或样式
val fontFamily: FontFamily? = null, // 要使用的字体
val fontFeatureSettings: String? = null, // 字体提供的高级字体设置
// 格式与CSS font-feature-settings属性相同
val letterSpacing: TextUnit = TextUnit.Unspecified, // 字母间距要增加的量
val baselineShift: BaselineShift? = null,// 文本从当前基线上移的量
val textGeometricTransform: TextGeometricTransform? = null,// 几何变换应用了文本
val localeList: LocaleList? = null, // 用于选择特定区域的字形的语言环境列表
val background: Color = Color.Unspecified,// 文本的背景颜色
val textDecoration: TextDecoration? = null,// 要在文字上绘制装饰(比如下划线)
val shadow: Shadow? = null,// 阴影效果
val textAlign: TextAlign? = null,// 文本在段落中的对齐方式
val textDirection: TextDirection? = null,// 用于解析最终文本和段落方向的算法:从左到右或从右到左
val lineHeight: TextUnit = TextUnit.Unspecified,// 行高
val textIndent: TextIndent? = null // 该段的缩进
)
通过上面的注释,我们对TextStyle就有了一个更深的了解了。 字体的使用:
Text(text = "Hello $name!", color = MaterialTheme.colors.primary,
style = MaterialTheme.typography.body1)
如果希望应用程序只使用同一种字体时,我们需要指定defaultFontFamily参数, 并省略所有TextStyle元素的fontFamily:
val Typography = Typography(defaultFontFamily = FontFamily.Default)
MaterialTheme(
...
typography = Typography,
...
)
4. 形状设置
Compose主题中提到的参数就剩下最后一个了---Shape。在Compose中形状也可以用代码来设置了,我们打开ui.theme包下面的Shape.kt文件:
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)
这是我们创建项目的时候,系统默认帮我们创建的代码。
看着眼熟吧,感觉和Colors一样,那我们先到Shapes类中去看看:
@Immutable
class Shapes(
// Button或Snackbar之类的小组件使用的形状
val small: CornerBasedShape = RoundedCornerShape(4.dp),
// Card 或 AlertDialog 等 中等组件使用的形状
val medium: CornerBasedShape = RoundedCornerShape(4.dp),
// 大型组件(例如 ModalDrawer或ModalBottomSheetLayout)使用的形状
val large: CornerBasedShape = RoundedCornerShape(0.dp)
)
用法与前面的颜色和字体一样
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background,
shape = MaterialTheme.shapes.medium
)
我们发现,不管颜色、字体还是形状都是用MaterialTheme去调用的,下面我们看一下MaterialTheme的源码:
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这个单例中,所以我们使用的时候直接通过MaterialTheme调用即可。
后续我们会正式进入Compose控件的学习与实践了。期待中(#^.^#)
最后
感谢你看到最后, 最后再说两点~
①如果你持有不同的看法,欢迎你在文章下方进行留言、评论。
②如果对你有帮助,或者你认可的话,欢迎给个小点赞,支持一下~
我是沐小枫,一个热爱学习、热爱编程的山东人
(文章内容仅供学习参考, 如有侵权,非常抱歉,请立即联系作者删除。)