1. 项目概述:安卓值日排班表单App
最近用Kotlin+Jetpack Compose完成了一个值日排班表单应用,这种工具类App在学校、公司、社区等场景特别实用。不同于传统排班表需要打印张贴或群内接龙,这个App能实现自动轮换、历史记录查询、多人协作等功能。选择Compose是因为它声明式UI开发效率比XML布局高3倍以上,而Kotlin的空安全特性让代码更健壮。
这个项目涉及几个关键技术点:
- Compose的LazyColumn实现可滚动排班列表
- ViewModel管理排班数据状态
- Room数据库持久化存储
- 手势交互实现排班项拖动排序
2. 技术架构设计
2.1 为什么选择Kotlin+Compose组合
相比Java+XML的传统方案,这个技术栈有三大优势:
- 开发效率:Compose的@Preview功能可以实时预览UI修改,省去编译运行时间
- 代码量:同样功能下代码量减少约40%
- 性能:Compose的智能重组机制只更新变化的UI部件
// 典型Compose组件示例 @Composable fun DutyItem(name: String) { Card( modifier = Modifier .padding(8.dp) .fillMaxWidth() ) { Text(text = name, style = MaterialTheme.typography.h6) } }2.2 数据层设计
采用MVVM架构,数据流走向如下:
UI层(Composable) ←→ ViewModel ←→ Repository ←→ Room数据库关键配置:
// Room数据库配置 @Database(entities = [DutySchedule::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun dutyDao(): DutyDao } // 定义数据实体 @Entity data class DutySchedule( @PrimaryKey val id: Int, val personName: String, val dutyDate: Long, val completed: Boolean = false )3. 核心功能实现
3.1 排班表UI构建
使用LazyColumn实现可滚动列表,配合rememberSaveable保存滚动位置:
@Composable fun DutyList(schedules: List<DutySchedule>) { LazyColumn( modifier = Modifier .fillMaxSize() .padding(16.dp) ) { items(schedules) { schedule -> DutyItem(schedule.personName) Divider() } } }3.2 拖动排序功能
通过Modifier.draggable实现项目拖动:
var currentIndex by remember { mutableStateOf(-1) } LazyColumn { itemsIndexed(items) { index, item -> Box( modifier = Modifier .draggable( state = rememberDraggableState { delta -> // 处理拖动逻辑 } ) ) { DutyItem(item) } } }3.3 自动排班算法
实现公平轮换的核心逻辑:
fun generateSchedule(people: List<String>, weeks: Int): List<DutySchedule> { val schedule = mutableListOf<DutySchedule>() repeat(weeks) { week -> val personIndex = week % people.size schedule.add(DutySchedule( id = week, personName = people[personIndex], dutyDate = System.currentTimeMillis() + week * 604800000 )) } return schedule }4. 关键问题解决方案
4.1 性能优化
问题:当排班项超过100个时出现卡顿
解决方案:
- 使用derivedStateOf减少不必要的重组
- 对LazyColumn启用itemKey参数
LazyColumn { items( items = schedules, key = { it.id } // 添加唯一key ) { item -> DutyItem(item) } }4.2 数据同步冲突
场景:多人同时修改排班表时
处理方案:
- 使用Room的@Update冲突策略
- 添加lastModified时间戳字段
@Update(onConflict = OnConflictStrategy.REPLACE) suspend fun updateSchedule(schedule: DutySchedule)5. 扩展功能实现
5.1 通知提醒
使用WorkManager实现定时提醒:
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val notificationWork = PeriodicWorkRequestBuilder<NotificationWorker>( 7, TimeUnit.DAYS // 每周提醒 ).setConstraints(constraints) .build() WorkManager.getInstance(context).enqueue(notificationWork)5.2 数据导出
将排班表导出为CSV文件:
fun exportToCsv(schedules: List<DutySchedule>): File { val file = File(context.getExternalFilesDir(null), "schedule.csv") file.bufferedWriter().use { writer -> writer.write("Name,Date,Completed\n") schedules.forEach { writer.write("${it.personName},${it.dutyDate},${it.completed}\n") } } return file }6. 测试与调试技巧
6.1 UI测试方案
使用Compose测试库验证界面行为:
@Test fun dutyList_DisplayCorrectItems() { composeTestRule.setContent { DutyList(testSchedules) } // 验证显示的item数量 composeTestRule.onAllNodesWithText("张三") .assertCountEquals(1) }6.2 数据库调试
通过Android Studio的Database Inspector实时查看Room数据:
提示:需要开启调试模式并在build.gradle中添加:
android.debug.obtainSharedPreferences=true
7. 项目优化方向
- 多设备同步:考虑接入Firebase实现云端同步
- 自定义规则:支持节假日排除等特殊排班规则
- 可视化统计:使用MPAndroidChart展示值日完成情况
这个项目让我深刻体会到Compose在表单类应用开发中的优势。特别是状态管理变得非常直观,不再需要处理繁琐的findViewById和回调接口。下一步计划加入小组件功能,让排班信息可以直接显示在桌面。