本文同步发表于我的微信公众号,在微信搜索
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)计时状态(是否已开始)
倒计时进度条
1@Composable 2fun counterDown(modifier: Modifier, seconds: Int) { 3 ConstraintLayout( 4 modifier = modifier 5 .fillMaxWidth() 6 .fillMaxHeight() 7 ) { 8 val viewModel: MainViewModel = viewModel() 9 val (progressBackground, progress, countdownTimer) = createRefs() 10 11 CircularProgressIndicator( 12 1f, 13 modifier = Modifier 14 .constrainAs(progressBackground) { 15 top.linkTo(parent.top) 16 start.linkTo(parent.start) 17 end.linkTo(parent.end) 18 bottom.linkTo(parent.bottom) 19 } 20 .height(300.dp) 21 .width(300.dp), 22 strokeWidth = 32.dp, color = Color.LightGray 23 ) 24 25 Progress( 26 Modifier 27 .constrainAs(progress) { 28 top.linkTo(parent.top) 29 start.linkTo(parent.start) 30 end.linkTo(parent.end) 31 bottom.linkTo(parent.bottom) 32 } 33 .height(300.dp) 34 .width(300.dp), 35 countdownSeconds = viewModel.clock, totalSeconds = viewModel.totalClock 36 ) 37 38 Row( 39 modifier = Modifier 40 .fillMaxWidth() 41 .constrainAs(countdownTimer) { 42 top.linkTo(parent.top) 43 start.linkTo(parent.start) 44 end.linkTo(parent.end) 45 bottom.linkTo(parent.bottom) 46 }, 47 horizontalArrangement = Arrangement.Center, 48 verticalAlignment = Alignment.CenterVertically 49 ) { 50 Text( 51 text = seconds.div(3600).toString(), 52 fontSize = 48.sp, 53 fontFamily = FontFamily.Monospace, 54 fontStyle = FontStyle.Normal, 55 fontWeight = FontWeight.Bold 56 ) 57 Text( 58 text = ":", 59 fontSize = 48.sp, 60 fontFamily = FontFamily.Monospace, 61 fontStyle = FontStyle.Normal, 62 fontWeight = FontWeight.Bold 63 ) 64 Text( 65 text = seconds.rem(3600).div(60).toString(), 66 fontSize = 48.sp, 67 fontFamily = FontFamily.Monospace, 68 fontStyle = FontStyle.Normal, 69 fontWeight = FontWeight.Bold 70 ) 71 Text( 72 text = ":", 73 fontSize = 48.sp, 74 fontFamily = FontFamily.Monospace, 75 fontStyle = FontStyle.Normal, 76 fontWeight = FontWeight.Bold 77 ) 78 Text( 79 text = seconds.rem(60).toString(), 80 fontSize = 48.sp, 81 fontFamily = FontFamily.Monospace, 82 fontStyle = FontStyle.Normal, 83 fontWeight = FontWeight.Bold 84 ) 85 } 86 } 87}
设置时长
1@Composable 2fun timePicker(modifier: Modifier) { 3 val viewModel: MainViewModel = viewModel() 4 Row( 5 modifier = modifier 6 .fillMaxWidth(), 7 horizontalArrangement = Arrangement.SpaceAround 8 ) { 9 Column( 10 verticalArrangement = Arrangement.Center, 11 horizontalAlignment = Alignment.CenterHorizontally 12 ) { 13 IconButton( 14 onClick = { viewModel.incHour() }, 15 Modifier 16 .background( 17 Color.LightGray, 18 shape = shapes.large 19 ) 20 .size(32.dp, 32.dp), 21 ) { 22 Icon( 23 painter = painterResource(id = R.drawable.ic_inc), 24 contentDescription = null, 25 modifier = Modifier 26 .size(18.dp), 27 tint = Color.Black 28 ) 29 } 30 Text( 31 text = viewModel.hours.toString(), 32 fontSize = 32.sp, 33 fontFamily = FontFamily.Serif, 34 fontStyle = FontStyle.Normal, 35 fontWeight = FontWeight.Bold, 36 modifier = Modifier.padding(0.dp, 8.dp) 37 ) 38 IconButton( 39 onClick = { viewModel.decHour() }, 40 Modifier 41 .background( 42 Color.LightGray, 43 shape = shapes.large 44 ) 45 .size(32.dp, 32.dp), 46 ) { 47 Icon( 48 painter = painterResource(id = R.drawable.ic_dec), 49 contentDescription = null, 50 modifier = Modifier 51 .size(18.dp), 52 tint = Color.Black 53 ) 54 } 55 } 56 Column( 57 verticalArrangement = Arrangement.Center, 58 horizontalAlignment = Alignment.CenterHorizontally 59 ) { 60 IconButton( 61 onClick = { viewModel.incMinute() }, 62 Modifier 63 .background( 64 Color.LightGray, 65 shape = shapes.large 66 ) 67 .size(32.dp, 32.dp), 68 ) { 69 Icon( 70 painter = painterResource(id = R.drawable.ic_inc), 71 contentDescription = null, 72 modifier = Modifier 73 .size(18.dp), 74 tint = Color.Black 75 ) 76 } 77 Text( 78 text = viewModel.minutes.toString(), 79 fontSize = 32.sp, 80 fontFamily = FontFamily.Serif, 81 fontStyle = FontStyle.Normal, 82 fontWeight = FontWeight.Bold, 83 modifier = Modifier.padding(0.dp, 8.dp) 84 ) 85 IconButton( 86 onClick = { viewModel.decMinute() }, 87 Modifier 88 .background( 89 Color.LightGray, 90 shape = shapes.large 91 ) 92 .size(32.dp, 32.dp), 93 ) { 94 Icon( 95 painter = painterResource(id = R.drawable.ic_dec), 96 contentDescription = null, 97 modifier = Modifier 98 .size(18.dp), 99 tint = Color.Black 100 ) 101 } 102 } 103 Column( 104 verticalArrangement = Arrangement.Center, 105 horizontalAlignment = Alignment.CenterHorizontally 106 ) { 107 IconButton( 108 onClick = { viewModel.incSecond() }, 109 Modifier 110 .background( 111 Color.LightGray, 112 shape = shapes.large 113 ) 114 .size(32.dp, 32.dp), 115 ) { 116 Icon( 117 painter = painterResource(id = R.drawable.ic_inc), 118 contentDescription = null, 119 modifier = Modifier 120 .size(18.dp), 121 tint = Color.Black 122 ) 123 } 124 Text( 125 text = viewModel.seconds.toString(), 126 fontSize = 32.sp, 127 fontFamily = FontFamily.Serif, 128 fontStyle = FontStyle.Normal, 129 fontWeight = FontWeight.Bold, 130 modifier = Modifier.padding(0.dp, 8.dp) 131 ) 132 IconButton( 133 onClick = { viewModel.decSecond() }, 134 Modifier 135 .background( 136 Color.LightGray, 137 shape = shapes.large 138 ) 139 .size(32.dp, 32.dp), 140 ) { 141 Icon( 142 painter = painterResource(id = R.drawable.ic_dec), 143 contentDescription = null, 144 modifier = Modifier 145 .size(18.dp), 146 tint = Color.Black 147 ) 148 } 149 } 150 } 151}
重置计时、开始计时
1@Composable 2fun bottomMenu(modifier: Modifier) { 3 val viewModel: MainViewModel = viewModel() 4 Row( 5 modifier = modifier.fillMaxWidth(), 6 horizontalArrangement = Arrangement.SpaceEvenly, 7 verticalAlignment = Alignment.CenterVertically 8 ) { 9 IconButton(onClick = { viewModel.reset() }) { 10 Icon( 11 painter = painterResource(id = R.drawable.ic_reset), contentDescription = null, 12 modifier = Modifier 13 .size(32.dp), 14 tint = Color.Black 15 ) 16 } 17 18 if (!viewModel.started) { 19 IconButton(onClick = { viewModel.startCountdown() }) { 20 Icon( 21 painter = painterResource(id = R.drawable.ic_start), contentDescription = null, 22 modifier = Modifier 23 .size(32.dp), 24 tint = Color.Black 25 ) 26 } 27 } 28 } 29}
效果


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