第六章:UI组件与Material3主题

第六章:UI 组件与 Material3 主题

Material3 是 Android 最新设计语言,Compose 原生支持,主题配置更简单。


6.1 主题配置

kotlin 复制代码
// Theme.kt
@Composable
fun MyFirstComposeTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    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,
    )
}

主题特点:

  • dynamicColor --- Android 12+ 支持从壁纸提取颜色
  • darkTheme --- 自动跟随系统深色模式
  • colorScheme --- Material3 颜色体系(primary/secondary/tertiary)

6.2 颜色定义

kotlin 复制代码
// Color.kt
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)

val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
颜色角色 浅色模式 深色模式
primary Purple40 Purple80
secondary PurpleGrey40 PurpleGrey80
tertiary Pink40 Pink80

6.3 通用组件:ArticleItem

列表卡片组件:

kotlin 复制代码
@Composable
fun ArticleItem(
    title: String,
    description: String?,
    thumbnail: String?,
    onClick: () -> Unit,
) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(12.dp)
            .clickable(onClick = onClick),
    ) {
        Column {
            ArticleImage(
                imageUrl = thumbnail.orEmpty(),
                contentDescription = title,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(180.dp),
            )
            Text(text = title, fontWeight = FontWeight.Bold)
            Text(text = description ?: "暂无描述")
        }
    }
}

Card 特点:

  • 带圆角和阴影的容器
  • modifier 配置点击事件
  • 内部 Column 竖向排列子组件

6.4 图片组件:ArticleImage

预览模式下显示占位图,运行时加载网络图片:

kotlin 复制代码
@Composable
fun ArticleImage(
    imageUrl: String,
    modifier: Modifier = Modifier,
    contentDescription: String? = null,
) {
    if (LocalInspectionMode.current) {
        // Preview 模式:显示占位图,不发网络请求
        Box(
            modifier = modifier.background(MaterialTheme.colorScheme.surfaceVariant),
            contentAlignment = Alignment.Center,
        ) {
            Icon(Icons.Default.Image, contentDescription = contentDescription)
        }
    } else {
        // 运行时:使用 Coil 加载网络图片
        AsyncImage(
            model = imageUrl,
            contentDescription = contentDescription,
            modifier = modifier,
            contentScale = ContentScale.Crop,
        )
    }
}

LocalInspectionMode:

  • Composable 在 Android Studio Preview 时为 true
  • 用于区分预览环境与真实运行环境

6.5 轮播图组件:BannerView

使用 HorizontalPager 实现自动轮播:

kotlin 复制代码
@Composable
fun BannerView(images: List<String>) {
    if (images.isEmpty()) return
    
    val pagerState = rememberPagerState(pageCount = { images.size })
    
    LaunchedEffect(Unit) {
        while (true) {
            delay(3000)
            val nextPage = (pagerState.currentPage + 1) % images.size
            pagerState.animateScrollToPage(nextPage)
        }
    }
    
    Box {
        HorizontalPager(state = pagerState, modifier = Modifier.height(200.dp)) { page ->
            AsyncImage(model = images[page], contentDescription = null)
        }
        // 底部指示器
        Row(modifier = Modifier.align(Alignment.BottomCenter)) {
            repeat(images.size) { index ->
                Box(modifier = Modifier
                    .size(8.dp)
                    .clip(CircleShape)
                    .background(if (pagerState.currentPage == index) Color.White else Color.Gray))
            }
        }
    }
}

关键点:

  • rememberPagerState 保存分页状态
  • LaunchedEffect 启动协程控制自动轮播
  • 页面指示器根据当前页切换颜色

6.6 头像组件:ProfileAvatar

kotlin 复制代码
@Composable
fun ProfileAvatar(avatarUrl: String, modifier: Modifier = Modifier) {
    if (LocalInspectionMode.current) {
        Box(
            modifier = modifier.clip(CircleShape).background(primary.copy(alpha = 0.15f)),
            contentAlignment = Alignment.Center,
        ) {
            Icon(Icons.Default.Person, modifier = Modifier.size(48.dp))
        }
    } else {
        AsyncImage(
            model = avatarUrl,
            contentDescription = "用户头像",
            modifier = modifier.clip(CircleShape),
            contentScale = ContentScale.Crop,
        )
    }
}

6.7 主题使用示例

kotlin 复制代码
@Composable
fun DetailArticleBody(article: ArticleBean) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = article.title,
            style = MaterialTheme.typography.headlineSmall,  // 使用主题字体
            fontWeight = FontWeight.Bold,
        )
        Text(
            text = "¥${"%.2f".format(article.price)}",
            color = MaterialTheme.colorScheme.primary,  // 使用主题颜色
            fontSize = 22.sp,
        )
        Card(
            colors = CardDefaults.cardColors(
                containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
            ),
        ) { /* ... */ }
    }
}

6.8 总结

  • Material3 主题支持动态颜色(Android 12+)
  • LocalInspectionMode 区分预览/运行模式
  • Coil 异步加载网络图片
  • HorizontalPager 实现轮播
  • Card 提供圆角阴影容器
  • 颜色/字体均通过 MaterialTheme 主题统一管理

上一章:第五章:数据层 --- 网络请求与 Repository 下一章:第七章:状态管理实战与架构总结

相关推荐
张拭心几秒前
Android 17 新特性:后台音频交互限制加强
android·前端
张拭心9 分钟前
Android 17 新特性:ProfilingManager 新触发器
android·前端
张拭心21 分钟前
Android 17 新特性:MessageQueue 无锁实现
android·前端
brycegao23 分钟前
如何搭建标准化 Git 工具流,保障 Android 团队代码质量
android·ci/cd
AI科技星24 分钟前
数术江湖·全卷合集 - 硬核江湖・数理史诗
android·人工智能·架构·概率论·学习方法
五月君_31 分钟前
安卓也支持了!微信链接 Claude Code 保姆级教程
android·微信
柚鸥ASO优化32 分钟前
一篇讲透安卓ASO!开发者千万别只盯着iOS了
android·ios·aso优化
木易 士心33 分钟前
compileSdkVersion、minSdkVersion 和 targetSdkVersion —— Android 三个核心的 SDK 版本配置
android
人道领域34 分钟前
为什么iPhone微信聊天记录搜不到“?“,而安卓可以。
android·微信·iphone
火山上的企鹅1 小时前
Codex实战:APP远程升级服务搭建(四)Node 服务端自动识别 APK 信息
android·服务器·git·github·qgc