《Jetpack Compose系列学习》-5 Compose的主题、字体和形状Shape

主题

我们通过第一个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调用即可。

猜你喜欢

转载自juejin.im/post/7074551404207013924
今日推荐