Jetpack系列之Compose Text

(一)概述

Jetpack Compose 是 Android 官方推出的 UI 工具包,用来以声明式方式构建界面,逐步取代传统的 XML + View。简而言之就是以Kotlin DSL代码声明UI状态,Compose框架会自动渲染界面。

Kotlin 复制代码
@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name")
}
说明:参数name 变了,UI 自动更新, 不需要 再findViewById / setText
Kotlin 复制代码
var count by remember { mutableStateOf(0) }

Button(onClick = { count++ }) {
    Text("Count: $count")
}
说明:状态驱动 UI(State-driven),count是状态,使用remember后,同一个compose方法中的count会进行缓存,状态变化时,会自动更新count状态值,进而更新UI。

(二)Compose的作用

1. 降低代码量

例如一个按钮点击,传统的xml layout等方式,需要编写xml 组件等,然后findViewById,setOnClickListener,setText等代码量大,而Compose极其简单:

Kotlin 复制代码
@Composable
fun AppButton(text: String, onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text(text)
    }
}
说明:所有组件均以@Composable标记,且只能在 Composable 里调用 Composable,本质是 UI 的最小可复用单元
2. UI与状态一致

原来xml的方式容易出现UI与数据不同步等问题,而Compose UI只来源于State,不会出现UI数据不同步问题。

3. 高度可复用、可组合
Kotlin 复制代码
如:Column {
    AppTopBar()
    Content()
    AppButton()
}

UI 像搭积木,比自定义 View 更简单,比 include/layout 更灵活
4.支持即时预览
Kotlin 复制代码
@Preview
@Composable
fun PreviewGreeting() {
    Greeting("Compose")
}
说明:使用@Preview 不用每修改一点编译一次App, Compose支持修改组件后 即时预览UI效果。

关于Compose的作用和好处还有很多,例如,单元/UI测试, 与 Kotlin / Flow / Coroutines 天然契合等等,后面再详细介绍。

(三)Text 封装

1. Compose 特性声明
Groovy 复制代码
buildFeatures {
   compose = true
}
说明:在 build.gradle.kts 声明支持Compose特性
2. Text 组件封装
Kotlin 复制代码
package com.leo.wechat.ui.theme

import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext

private val DarkColorScheme = darkColorScheme(
    primary = WeChatGreen,
    onPrimary = Color.Black,
    secondary = WeChatGreen,
    onSecondary = Color.Black,
    background = WeChatDarkBackground,
    onBackground = WeChatDarkTextPrimary,
    surface = WeChatDarkSurface,
    onSurface = WeChatDarkTextPrimary,
    outline = WeChatDarkDivider,
    error = WeChatDarkError,
    tertiary = WeChatDarkWarning
)

private val LightColorScheme = lightColorScheme(
    // 主色:Button、Checkbox...强调动作等默认背景
    primary = WeChatGreen,
    // primary内容颜色:Button的文字,primary背景上的Icon
    onPrimary = Color.White,

    // 次强调颜色:次级按钮,Toggle,Chip
    secondary = WeChatGreen,
    // secondary内容颜色
    onSecondary = Color.White,

    // 页面底色:Scaffold.containerColor,页面根布局,List背景
    background = WeChatLightBackground,
    // 页面文字颜色:页面标题,普通正文Text,TopBar标题
    onBackground = WeChatLightTextPrimary,

    // 卡片/列表项/浮层 背景色:Card,ListItem,BottomSheet,Dialog
    surface = WeChatLightSurface,
    // surface 上的文字/图标颜色:列表项文字,Card 内容,Dialog 内容
    onSurface = WeChatLightTextPrimary,

    // 分割线/边框/描边颜色:Divider,OutlinedButton,输入框边框
    outline = WeChatLightDivider,

    // 错误/风险 背颜色
    error = WeChatLightError,
    // 警告/提醒 背颜色
    tertiary = WeChatLightWarning
)

/* Compose Theme 才是真正的 UI 主题 */
@Composable
fun ComposeTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = false,
    content: @Composable () -> Unit,
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }

        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,// 所有颜色
        typography = Typography,// 所有字体
        content = content
    )
}

说明:编写 APP 主题,主要用于统一各组件使用到的不同颜色和字体及深浅色模式下的切换。
Kotlin 复制代码
package com.leo.wechat.ui.theme

import androidx.compose.ui.graphics.Color

// Main Color
val WeChatGreen = Color(0xFF07C160)

// LightColor Mode
val WeChatLightBackground = Color(0xFFF7F7F7)
val WeChatLightSurface = Color(0xFFFFFFFF)
val WeChatLightTextPrimary = Color(0xFF191919)
val WeChatLightTextSecondary = Color(0xFF888888)
val WeChatLightDivider = Color(0xFFE5E5E5)
val WeChatLightError = Color(0xFFF53F3F)
val WeChatLightWarning = Color(0xFFFFA940)

// DarkColor Mode
val WeChatDarkBackground = Color(0xFF111111)
val WeChatDarkSurface = Color(0xFF1C1C1E)
val WeChatDarkTextPrimary = Color(0xFFEDEDED)
val WeChatDarkTextSecondary = Color(0xFF9B9B9B)
val WeChatDarkDivider = Color(0xFF2C2C2E)
val WeChatDarkError = Color(0xFFFF6B6B)
val WeChatDarkWarning = Color(0xFFFFC069)

说明:定义应用中用到主要颜色等
Kotlin 复制代码
package com.leo.wechat.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.leo.wechat.R

// Set of Font Family
val Bariol = FontFamily(
    Font(R.font.bariol_regular, FontWeight.Normal),
    Font(R.font.bariol_bold, FontWeight.Bold),
)

// Set of Material typography styles to start with
val Typography = Typography(
    // 大标题(页面标题)
    titleLarge = TextStyle(
        fontSize = 28.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Bold,
        lineHeight = (28 * 0.9).sp
    ),
    // 中标题(Section / AppBar)
    titleMedium = TextStyle(
        fontSize = 24.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Bold,
        lineHeight = (24 * 0.9).sp
    ),
    // 小标题(列表标题)
    titleSmall = TextStyle(
        fontSize = 20.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Medium,
        lineHeight = (20 * 0.9).sp
    ),
    // 正文主内容
    bodyLarge = TextStyle(
        fontSize = 18.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Normal,
        lineHeight = (18 * 0.9).sp
    ),
    // 次要正文
    bodyMedium = TextStyle(
        fontSize = 16.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Normal,
        lineHeight = (16 * 0.9).sp
    ),
    // 辅助说明 / 时间戳
    bodySmall = TextStyle(
        fontSize = 14.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Normal,
        lineHeight = (14 * 0.9).sp
    ),
    // 按钮 / 标签
    labelLarge = TextStyle(
        fontSize = 16.sp,
        fontFamily = Bariol,
        fontWeight = FontWeight.Medium
    )
)

说明:定义应用中用到的主要字体样式等
Kotlin 复制代码
package com.leo.wechat.ui.component.text

sealed class AppTextState {
    data object Normal : AppTextState()
    data object Disabled : AppTextState()
    data object Error : AppTextState()
    data object Warning : AppTextState()
    data object Success : AppTextState()
}

说明:定义应用中的Text几种状态
Kotlin 复制代码
package com.leo.wechat.ui.component.text

import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color

object AppTextColor {

    const val DisabledAlpha = 0.38f

    @Composable
    fun normal(base: Color): Color = base

    @Composable
    fun disabled(): Color =
        MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledAlpha)

    @Composable
    fun error(): Color =
        MaterialTheme.colorScheme.error

    @Composable
    fun warning(): Color =
        MaterialTheme.colorScheme.tertiary

    @Composable
    fun success(): Color =
        MaterialTheme.colorScheme.primary
}

说明:定义应用中Text不同状态下对应的颜色
Kotlin 复制代码
package com.leo.wechat.ui.component.text

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit

@Composable
internal fun AppTextState.resolveColor(
    baseColor: Color,
): Color = when (this) {
    AppTextState.Normal -> AppTextColor.normal(baseColor)
    AppTextState.Disabled -> AppTextColor.disabled()
    AppTextState.Error -> AppTextColor.error()
    AppTextState.Warning -> AppTextColor.warning()
    AppTextState.Success -> AppTextColor.success()
}

private fun AppTextState.resolveStyle(base: TextStyle): TextStyle =
    when (this) {
        AppTextState.Error, AppTextState.Warning ->
            base.copy(fontWeight = FontWeight.Medium)

        else -> base
    }

@Composable
fun AppText(
    text: String,
    modifier: Modifier = Modifier,
    state: AppTextState = AppTextState.Normal,
    color: Color = MaterialTheme.colorScheme.onBackground,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign = TextAlign.Unspecified,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,// word wrap
    maxLines: Int = Int.MAX_VALUE,
    minLines: Int = 1,
    onTextLayout: ((TextLayoutResult) -> Unit)? = null,
    textStyle: TextStyle = MaterialTheme.typography.bodyLarge,
) {
    Text(
        text = text,
        modifier = modifier,
        color = state.resolveColor(color),
        letterSpacing = letterSpacing,
        textDecoration = textDecoration,
        textAlign = textAlign,
        lineHeight = lineHeight,
        overflow = overflow,
        softWrap = softWrap,
        maxLines = maxLines,
        minLines = minLines,
        onTextLayout = onTextLayout,
        style = state.resolveStyle(textStyle)
    )
}

说明:定义一个应用中通用的Text,支持不同状态下的颜色样式等。
相关推荐
用户985120035831 天前
Compose案例 — Android 调用系统相机拍照
android jetpack
黄林晴1 天前
告别手写延迟!Android Ink API 1.0 正式版重磅发布,4ms 极致体验触手可及
android·android jetpack
黄林晴2 天前
Compose Multiplatform 1.10.0 重磅发布!三大核心升级,跨平台开发效率再提升
android·android jetpack
Jony_3 天前
Android 设计架构演进历程
android·android jetpack
我命由我123453 天前
Android 项目路径包含非 ASCII 字符问题:Your project path contains non-ASCII characters
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
ljt27249606616 天前
Compose笔记(六十八)--MutableStateFlow
android·笔记·android jetpack
zFox7 天前
三、Kotlin协程+异步加载+Loading状态
kotlin·android jetpack·协程
我命由我123457 天前
Kotlin 面向对象 - 装箱与拆箱
android·java·开发语言·kotlin·android studio·android jetpack·android-studio
我命由我123457 天前
Android Jetpack Compose - Snackbar、Box
android·java·java-ee·kotlin·android studio·android jetpack·android-studio