之前有用JavaFx来写一些小组件,可以参看我之前的文档JAVAFX小工具,总感觉写起来不是那么顺手。一直在找一些替代方案,有想过使用electron来实现。偶然发现可以使用KMP(kotlin multipart platform)来实现,Compose正好也是支持桌面版的编写。
之前一直是写JAVA,Compose使用的是Kotlin来编写,在函数使用上会比较便捷。决定先写一个时钟来练一下手。
构思
效果
时钟的大体框架
步骤大体可以分为下面几部
- 先画一个表盘
- 在画一个中心点
- 在画时针,分针,秒针
- 想办法这三个针动起来
- 初始化对应时间,赋值这三个针上
表盘
这里采用Row进行布局,画原型表盘时,需要用到canvas来进行绘制。Compose同理也是支持的,也提供了非常便捷的操作。
ini
Canvas(modifier = Modifier.size(200.dp, 200.dp)
,
onDraw = {
// Head
drawCircle(
Brush.linearGradient(
colors = listOf(Color.Red, Color.Green)
),
radius = size.width / 2,
center = center,
style = Stroke(width = 5.0f)
)
}
)
画了一个200*200的圆
中心点
ini
// 画一个中心圆点
drawCircle(
color = Color.Black,
radius = 4.0f,
center = center
)
这个指定对应的位置在正中心即可。
时针,分针,秒针
这时,需要用到sin,cos函数来计算对应的角度,通过圆的半径(100)来计算时针,分针,秒针对应的x,y轴的坐标。
ini
// 弧度值
val rad = Math.PI * 2 / 360 * localTime.second.toDouble() * 6
val length = (size.width / 2 - size.width * 0.075f * 0.5)
// 画秒针
drawLine(
color = Color.Black,
start = center,
end = center + Offset((Math.sin(rad) * length).toFloat(), (-Math.cos(rad) * length).toFloat()),
strokeWidth = 2.0f
)
ini
// 弧度值
val rad = Math.PI * 2 / 360 * (localTime.minute.toDouble() + localTime.second.toDouble() / 60) * 6
val length = size.width / 2 * 0.75f
// 画分针
drawLine(
color = Color.Black,
start = center,
end = center + Offset((Math.sin(rad) * length).toFloat(), (-Math.cos(rad) * length).toFloat()),
strokeWidth = 2.0f,
)
ini
// 弧度值
val rad = Math.PI * 2 / 360 * (localTime.hour.toDouble() + localTime.minute.toDouble() / 60 + localTime.second.toDouble() / 3600) * 30
val length = size.width / 2 * 0.5f
// 画时针
drawLine(
color = Color.Black,
start = center,
end = center + Offset((Math.sin(rad) * length).toFloat(), (-Math.cos(rad) * length).toFloat()),
strokeWidth = 3.0f,
)
计算分针时,需要叠加上秒针;计算时针角度时,需要叠加上分针和秒针。
画1~12点的刻度
这个地方有一个难点,Compose的canvas是不支持直接画文字的,这个时候就需要调用底层画布来进行绘制。同理也需要计算每个点对应的x,y轴坐标。
ini
// 画数字
for (i in 1..12) {
drawIntoCanvas {
// 弧度值
val rad = Math.PI * 2 / 12 * i
val length = size.width / 2 * 0.9f
it.nativeCanvas.drawString(
s = i.toString(),
x = center.x - 2 + (Math.sin(rad) * length).toFloat(),
y = center.y + 2 + (-Math.cos(rad) * length).toFloat(),
paint = org.jetbrains.skia.Paint().apply {
color = Color.Black.toArgb()
},
font = org.jetbrains.skia.Font(
typeface = org.jetbrains.skia.Typeface.makeDefault(),
size = 10.0f
)
)
}
}
动起来
我们表动起来需要达到两个目标,分别是根据时间自动旋转和无线循环。需要用到compose中的无线循环动画。
ini
// 秒针角度定义
// 设置初始值
val secondAngle by infiniteTransaction.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(60000, easing = LinearEasing)
)
)
// 分针角度定义
// 设置初始值
val minuteAngle by infiniteTransaction.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(3600000, easing = LinearEasing),
)
)
// 时针角度定义
// 设置初始值
val hourAngle by infiniteTransaction.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(3600000*12, easing = LinearEasing),
)
)
表盘初始值
我是通过localtime得到小时,分,秒。在分别计算对应的x,y轴的坐标。旋转画布达到想要的角度即可。
锦上添花
进行的标题栏的隐藏,背景透明,正好圣诞节加入了圣诞树的背景。正好祝掘友圣诞快乐^_^
Compose使用体验
使用上感觉非常流畅,由于是kotlin,代码量也极少。后续会用Compose来重构我之前写的JavaFx小工具。