本文同步发表于我的微信公众号,在微信搜索
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