• 推荐
  • 前端
  • 后端
  • Android
  • IOS
  • 人工智能

2021-09-02关注

初识 Jetpack Compose(四) :主题

compose-landing-roadmap.svg

目录

  • 初识 Jetpack Compose(一) :Hello,Jetpack Compose!
  • 初识 Jetpack Compose(二) :布局
  • 初识 Jetpack Compose(三) :修饰符(Modifier)
  • 初识 Jetpack Compose(四) :主题

一、主题

借助 Jetpack Compose,您可以通过应用主题,轻松地赋予应用一致的外观和风格。您可以自定义 Compose 的 Material Design 实现,使其适合您产品的品牌。如果这不符合您的需求,您可以使用 Compose 的公共 API 构建自定义设计系统。

当我们使用Android Studio创建一个Compose应用时,默认给我们创建了一个ui.theme包,包里面包含了Color.kt、Shape.kt、Theme.kt、Type.kt,而这刚好就是本文要讲到的主题。
下面,我们就跟着代码看一下:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTheme {
                    MainPage()
            }
        }
    }
}
复制代码

自动生成的MainActivity中结构大概是这样的,那么这个ComposeTheme是个什么东西呢?我们点进去看一下:

//暗黑主题颜色
private val DarkColorPalette = darkColors()
//亮色主题颜色
private val LightColorPalette = lightColors()

@Composable
fun ComposeTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable() () -> Unit
) {
    //根据系统主题模式指定colors的值
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }
    //声明主题
    MaterialTheme(
        colors = colors,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}
复制代码

了然,在上面说到的Theme.kt内,Android Studio为我们生成了支持跟随系统常规与暗黑模式切换的主题。
MaterialTheme是官方提供的一种主题实现,它由 由颜色、排版和形状属性组成。上面代码中的主题模式切换仅对颜色进行了处理。

1. 颜色(Colors)

那么接下来我们看看DarkColorPalette和LightColorPalette是什么?

//暗黑主题颜色
private val DarkColorPalette = darkColors()
//亮色主题颜色
private val LightColorPalette = lightColors()
复制代码

继续查看 darkColors() 和 lightColors(),以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
)
复制代码

他们都继承自Colors,并且在入参中对Colors什么的颜色属性进行了默认值赋予。

可以得出结论:Compose为我们默认提供了亮色模式和暗黑默认的两套Colors配置,常规情况下,我们只需要将不同模式下的颜色属性改变为我们想要的颜色即可。

可是Compose的Colors只提供了12个颜色,不够用或者提供的描述不够明确怎么办?
这个时候Kotlin的语法糖就极大的排上用上了,我们可以通过给Colors添加拓展属性来定义我们想要的颜色属性,比如:

val White = Color(0xFFFFFFFF)
val Black = Color(0xFF000000)
val Colors.Divider: Color
    @Composable get() = if (isLight) Black else White
    
    
//然后就可以愉快的使用了
Text(text = "RoundButton",color = MaterialTheme.colors.Divider)
复制代码

1.1. 值得一提

虽然 Material 是我们推荐的设计系统并且 Jetpack Compose 附带了 Material 的实现,但您并非只能使用它。您完全可以采用同样的方式创建自己的设计系统;Material 完全基于公共 API,您可以使用这些 API 来实现此目的。

虽然官方附带了 Material 的实现,但是也仅仅局限于亮色和暗黑模式的切换,要通过Material来实现各种主题,还是需要自定义的。

2. 排版(Typography)

我们来看看官方的描述

Compose 使用 Typography、TextStyle 和字体相关类来实现字型系统。Typography 构造函数可以提供每种样式的默认值

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
) {
...
}
复制代码

通过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,
    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
)
复制代码

想想XML中的TextStyle那寥寥的几个属性:

1630568678(1).jpg 终于不再为了UI要求的各种字重而使用画笔来加粗文字了,喜大普奔

同样,我们依旧可以通过为Typography添加拓展属性来定义我们自己的排版

val Typography.Title: TextStyle
    @Composable get() = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Bold,
        fontSize = 18.sp
    )

val Typography.SubText: TextStyle
    @Composable get() = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W500,
        fontSize = 16.sp
    )
val Typography.Content: TextStyle
    @Composable get() = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    )

val Typography.Description: TextStyle
    @Composable get() = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 14.sp
    )
   
复制代码
 //使用自定义的Typography属性
Column(
    Modifier
        .background(Color.White)
        .padding(20.dp)) {
    Text(text = "Title",style = MaterialTheme.typography.Title)
    Text(text = "SubText",style = MaterialTheme.typography.SubText)
    Text(text = "Content",style = MaterialTheme.typography.Content)
    Text(text = "Description",style = MaterialTheme.typography.Description)
}
复制代码

效果如下:

1630570244(1).jpg

3. 形状(Shapes)

Material 定义了一个形状系统,可让您定义大型、中型和小型组件的形状。

theme-shapes.png 与 Colors 、 Typography 一样,官方为我们提供了Shapes用来管理项目中使用到样式。
依旧来看看Shapes的源码:

@Immutable
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)
) {
...
}
复制代码

正如官方文档所说,Shapes提供了大、中、小三种形状配置,以便在同一语义下为不同大小的组件提供不同的样式;如果大、中、小满足不了你的需求,依旧可以给Shapes添加拓展属性来满足需求。
不过我认为Shapes在日常开发中意义不大,在编写页面时,直接在组件内使用各种Shape就好了,比如下面代码中 Shape的使用。

@Composable
fun RoundButton() {
    Box(
        modifier = Modifier
            .width(300.dp)
            .height(90.dp)
            .padding(20.dp)
            .background(Color(0xFF3ADF00), shape = RoundedCornerShape(50)),
       
        contentAlignment = Alignment.Center){
        Text(text = "RoundButton",color = Color.White)
    }
}
复制代码


Compose中shape有如下4个子类:

3.1. RoundedCornerShape

直接看效果RoundedCornerShape(50)

1630573677(1).jpg

3.2. CutCornerShape

直接看效果CutCornerShape(50)

1630573756(1).jpg

3.3. AbsoluteRoundedCornerShape

AbsoluteRoundedCornerShape和RoundedCornerShape差不多,官方的描述是:

描述带有圆角的矩形的形状。
此形状不会自动镜像 中的角大小LayoutDirection.Rtl,RoundedCornerShape用于此形状的布局方向感知版本。

3.4. AbsoluteCutCornerShape

AbsoluteCutCornerShape和CutCornerShape差不多,官方的描述是:

描述带有切角的矩形的形状。角尺寸代表切割长度 - 切割直角三角形的两条腿的大小。
此形状不会自动镜像 中的角大小LayoutDirection.Rtl,CutCornerShape用于此形状的布局方向感知版本。

二、最后

好记性不如烂笔头,初识 Jetpack Compose 系列是我自己的学习笔记,在加深知识巩固的同时,也可以锻炼一下写作技能。文章中的内容仅作参考,如有问题请留言指正。

1. 参考

  • Android developers
云星球用户

关于

京ICP备2021005633号-1

京公网安备11010502044331号

站长电话:18401753012

举报邮箱: 761292449@qq.com

云星球 @ 2025

生活影视公众号

circle-left3 circle-left4 new-tab github