主题
我们通过第一个Compose应用程序知道了其主题theme的设置
ComposeTheme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
Greeting("Android", true)
}
}
复制代码
这里面我们自定义一个theme来替代默认的theme主题:
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200
)
@Composable
fun FourTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(colors = colors, typography = Typography, shapes = Shapes, content = content)
}
复制代码
我们定义了两个colors,分别是深色模式和浅色模式的主题默认颜色。其中FourTheme是一个可组合函数,接收两个参数,第一个参数代表是否为深色模式,第二个参数是我们的布局,会自动根据手机当前是否是深色模式来选择需要使用的colors,完了设置到MaterialTheme中。MaterialTheme也是一个可组合函数,它有四个参数,除已知的两个参数外,还有typography和shapes,typography是一组文本样式,shapes是组件要使用的一组形状,后面会讲到。
@Composable
fun MaterialTheme(
colors: Colors = MaterialTheme.colors,
typography: Typography = MaterialTheme.typography,
shapes: Shapes = MaterialTheme.shapes,
content: @Composable () -> Unit
) { // 省略... }
复制代码
颜色设置
上面介绍的Colors并不是color,它是一个类,我们传入的颜色通过构造方法传入到Colors中,我们看到了熟悉的mutableStateOf,这里将Color转为Compose可观察的状态State。
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
// ...
}
复制代码
我们接下来再看看Color类:
inline class Color(val value: ULong) {
/**
* Returns this color's color space.
*
* @return A non-null instance of [ColorSpace]
*/
@Stable
val colorSpace: ColorSpace
get() = ColorSpaces.getColorSpace((value and 0x3fUL).toInt())
fun convert(colorSpace: ColorSpace): Color {
if (colorSpace == this.colorSpace) {
return this // nothing to convert
}
val connector = this.colorSpace.connect(colorSpace)
val color = getComponents()
connector.transform(color)
return Color(
red = color[0],
green = color[1],
blue = color[2],
alpha = color[3],
colorSpace = colorSpace
)
}
@Stable
val red: Float
get() {
return if ((value and 0x3fUL) == 0UL) {
((value shr 48) and 0xffUL).toFloat() / 255.0f
} else {
Float16(((value shr 48) and 0xffffUL).toShort())
.toFloat()
}
}
companion object {
@Stable
val Black = Color(0xFF000000)
@Stable
val DarkGray = Color(0xFF444444)
@Stable
val Gray = Color(0xFF888888)
// 省略...
}
// 省略...
复制代码
代码有部分删减。我们可以看到Color类是一个简单的数据存放类,来存放颜色值的,然后有ARGB的变量用来存放颜色相关的值,在伴生对象中定义了一些常用的颜色值供我们直接调用。
如果我们想要设定自己需要的颜色值,可以通过下面的方法实现:
@Stable
fun Color(
red: Float,
green: Float,
blue: Float,
alpha: Float = 1f,
colorSpace: ColorSpace = ColorSpaces.Srgb
): Color {
// 省略...
}
复制代码
还有像Color类中伴生对象中那样的颜色设置就可以调用下面的方法:
@Stable
fun Color(color: Long): Color {
return Color(value = (color.toULong() and 0xffffffffUL) shl 32)
}
复制代码
知道了上面的用法我们回头看看我们定义的FourTheme里面的相关主题色是怎么实现的,是不是很熟悉了:
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
复制代码
对,就是我们刚才说的Color的用法。设置主题色我们要注意需要给哪些属性设定颜色,Colors中都有默认值,我们来看看这里每个参数对应颜色的含义:
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调用即可
object MaterialTheme {
val colors: Colors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
// 省略...
}
复制代码
Text(
text = "Hello theming",
color = MaterialTheme.colors.primary
)
复制代码
字体设置
typography就是字体系统,比如字体样式、字体宽度和字体大小等,就像HTML中的H1、H2、H3等,设置好了使用非常方便,再使用的时候无须再定义,具体配置如下:
val Typography = Typography(
body1 = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
)
button = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W500,
fontSize = 14.sp
)
caption = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp
)
)
复制代码
上面就是创建新项目的时候系统自动生成的typography,这里我们重写了几个常用的字体,那么Typography中是什么呢,我们看看它的实现:
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
) {
// 省略...
}
复制代码
Typography类中可以设置很多字体,上面代码我们重写了其中的body1、button和caption的字体。那么TextStyle是什么呢?它描述了文字的各种属性,我们来看看:
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, // 字体提供的高级字体设置
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 // 该段的缩进
) {
// 省略...
}
复制代码
调用很简单
Text(
text = "Caption styled",
style = MaterialTheme.typography.caption
)
复制代码
如果我们想至始自终使用同一字体,我们可以指定defaultFontFamily参数,并省略所有的TextStyle元素的fontFamily:
val typography = Typography(defaultFontFamily = Rubik)
MaterialTheme(typography = typography, /*..*/)
复制代码
形状设置
之前在Android View中,我们定义shape需要在res目录的drawable文件夹下创建xml来使用,我们在Compose中也可以通过代码来设置,如:
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)
复制代码
其实它的套路和之前说的Colors基本一样,我们来卡看Shapes类:
class Shapes(
/**
* Shape used by small components like [Button] or [Snackbar]. Components like
* [FloatingActionButton], [ExtendedFloatingActionButton] use this shape, but override
* the corner size to be 50%. [TextField] uses this shape with overriding the bottom corners
* to zero.
*/
val small: CornerBasedShape = RoundedCornerShape(4.dp),
/**
* Shape used by medium components like [Card] or [AlertDialog].
*/
val medium: CornerBasedShape = RoundedCornerShape(4.dp),
/**
* Shape used by large components like [ModalDrawer] or [ModalBottomSheetLayout].
*/
val large: CornerBasedShape = RoundedCornerShape(0.dp)
) {
// 省略...
}
复制代码
这里定义了small、medium和large,使用上:
Surface(
shape = MaterialTheme.shapes.medium,/*...*/
)
复制代码
这么调用就可以了。上面讲到的colors、typography和shapes,会保存在MaterialTheme这个单例中,我们使用的时候直接通过MaterialTheme调用即可。