本文同步发表于我的微信公众号,在微信搜索
OpenCV or Android即可关注。
前言
最近Android官方发起了Jetpack Compose的推广活动:Jetpack Compose开发者挑战赛。活动时间一个月,每周一题,广大开发者根据官方需求,Clone官方模板工程并使用Jetpack Compose技术结题后按要求提交,即可参与活动。今天抽空完成了第二题,有点丑陋,感兴趣的同学可以做个漂亮点的。
环境
- 活动地址:https://developer.android.google.cn/dev-challenge
- 官方教程:https://developer.android.google.cn/jetpack/compose/documentation
- Github模板:https://github.com/android/android-dev-challenge-compose
- Android Studio (Canary build):https://developer.android.com/studio/preview
第二周
题目:Countdown Timer
要求:创建一个有效的单屏倒数计时器。
知识点:
- 状态:https://developer.android.google.cn//jetpack/compose/state
- 动画:https://developer.android.google.cn/jetpack/compose/animation
思路
简单的计时器操作:设置计时时长,开始计时,等待计时结束或者手动暂停计时。
- 界面元素:(1)倒计时进度条;(2)时分秒设置计时长度;(3)重置计时按钮、开始计时按钮
- 可变状态:(1)时长(时分秒);(2)倒计时进度与总时长;(3)计时状态(是否已开始)
倒计时进度条
@Composable fun counterDown(modifier: Modifier, seconds: Int) { ConstraintLayout( modifier = modifier .fillMaxWidth() .fillMaxHeight() ) { val viewModel: MainViewModel = viewModel() val (progressBackground, progress, countdownTimer) = createRefs() CircularProgressIndicator( 1f, modifier = Modifier .constrainAs(progressBackground) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) bottom.linkTo(parent.bottom) } .height(300.dp) .width(300.dp), strokeWidth = 32.dp, color = Color.LightGray ) Progress( Modifier .constrainAs(progress) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) bottom.linkTo(parent.bottom) } .height(300.dp) .width(300.dp), countdownSeconds = viewModel.clock, totalSeconds = viewModel.totalClock ) Row( modifier = Modifier .fillMaxWidth() .constrainAs(countdownTimer) { top.linkTo(parent.top) start.linkTo(parent.start) end.linkTo(parent.end) bottom.linkTo(parent.bottom) }, horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = seconds.div(3600).toString(), fontSize = 48.sp, fontFamily = FontFamily.Monospace, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold ) Text( text = ":", fontSize = 48.sp, fontFamily = FontFamily.Monospace, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold ) Text( text = seconds.rem(3600).div(60).toString(), fontSize = 48.sp, fontFamily = FontFamily.Monospace, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold ) Text( text = ":", fontSize = 48.sp, fontFamily = FontFamily.Monospace, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold ) Text( text = seconds.rem(60).toString(), fontSize = 48.sp, fontFamily = FontFamily.Monospace, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold ) } } }
设置时长
@Composable fun timePicker(modifier: Modifier) { val viewModel: MainViewModel = viewModel() Row( modifier = modifier .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround ) { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { IconButton( onClick = { viewModel.incHour() }, Modifier .background( Color.LightGray, shape = shapes.large ) .size(32.dp, 32.dp), ) { Icon( painter = painterResource(id = R.drawable.ic_inc), contentDescription = null, modifier = Modifier .size(18.dp), tint = Color.Black ) } Text( text = viewModel.hours.toString(), fontSize = 32.sp, fontFamily = FontFamily.Serif, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp, 8.dp) ) IconButton( onClick = { viewModel.decHour() }, Modifier .background( Color.LightGray, shape = shapes.large ) .size(32.dp, 32.dp), ) { Icon( painter = painterResource(id = R.drawable.ic_dec), contentDescription = null, modifier = Modifier .size(18.dp), tint = Color.Black ) } } Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { IconButton( onClick = { viewModel.incMinute() }, Modifier .background( Color.LightGray, shape = shapes.large ) .size(32.dp, 32.dp), ) { Icon( painter = painterResource(id = R.drawable.ic_inc), contentDescription = null, modifier = Modifier .size(18.dp), tint = Color.Black ) } Text( text = viewModel.minutes.toString(), fontSize = 32.sp, fontFamily = FontFamily.Serif, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp, 8.dp) ) IconButton( onClick = { viewModel.decMinute() }, Modifier .background( Color.LightGray, shape = shapes.large ) .size(32.dp, 32.dp), ) { Icon( painter = painterResource(id = R.drawable.ic_dec), contentDescription = null, modifier = Modifier .size(18.dp), tint = Color.Black ) } } Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { IconButton( onClick = { viewModel.incSecond() }, Modifier .background( Color.LightGray, shape = shapes.large ) .size(32.dp, 32.dp), ) { Icon( painter = painterResource(id = R.drawable.ic_inc), contentDescription = null, modifier = Modifier .size(18.dp), tint = Color.Black ) } Text( text = viewModel.seconds.toString(), fontSize = 32.sp, fontFamily = FontFamily.Serif, fontStyle = FontStyle.Normal, fontWeight = FontWeight.Bold, modifier = Modifier.padding(0.dp, 8.dp) ) IconButton( onClick = { viewModel.decSecond() }, Modifier .background( Color.LightGray, shape = shapes.large ) .size(32.dp, 32.dp), ) { Icon( painter = painterResource(id = R.drawable.ic_dec), contentDescription = null, modifier = Modifier .size(18.dp), tint = Color.Black ) } } } }
重置计时、开始计时
@Composable fun bottomMenu(modifier: Modifier) { val viewModel: MainViewModel = viewModel() Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically ) { IconButton(onClick = { viewModel.reset() }) { Icon( painter = painterResource(id = R.drawable.ic_reset), contentDescription = null, modifier = Modifier .size(32.dp), tint = Color.Black ) } if (!viewModel.started) { IconButton(onClick = { viewModel.startCountdown() }) { Icon( painter = painterResource(id = R.drawable.ic_start), contentDescription = null, modifier = Modifier .size(32.dp), tint = Color.Black ) } } } }
效果


源码
https://github.com/onlyloveyd/Jetpack-Compose-CountdownTimer
