第六章: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 下一章:第七章:状态管理实战与架构总结

相关推荐
TechMerger2 小时前
Android 17 重磅重构!服役 20 年的 MessageQueue 迎来无锁改造,卡顿大幅优化!
android·性能优化
测试员周周3 小时前
【Appium 系列】第18节-重试与容错 — 移动端测试的稳定性保障
人工智能·python·功能测试·ui·单元测试·appium·测试用例
yuhuofei20215 小时前
【Python入门】Python中字符串相关拓展
android·java·python
dalancon5 小时前
Android Input Spy Window
android
dalancon7 小时前
InputDispatcher派发事件,查找目标窗口
android
我命由我123457 小时前
Android Framework P3 - MediaServer 进程、认识 ServiceManager 进程
android·c语言·开发语言·c++·visualstudio·visual studio·android runtime
天才少年曾牛8 小时前
Android14 新增系统服务后,应用调用出现 “hidden api” 警告的原因与解决方案
android·frameworks
赏金术士8 小时前
Jetpack Compose 底部导航实战教程(完整版)
android·kotlin·compose
随遇丿而安8 小时前
第5周:XML 资源、样式和主题,真正解决的是“页面以后还改不改得动”
android