Compose Desktop实现一个圣诞树背景的时钟

之前有用JavaFx来写一些小组件,可以参看我之前的文档JAVAFX小工具,总感觉写起来不是那么顺手。一直在找一些替代方案,有想过使用electron来实现。偶然发现可以使用KMP(kotlin multipart platform)来实现,Compose正好也是支持桌面版的编写。

之前一直是写JAVA,Compose使用的是Kotlin来编写,在函数使用上会比较便捷。决定先写一个时钟来练一下手。

构思

效果

时钟的大体框架

步骤大体可以分为下面几部

  1. 先画一个表盘
  2. 在画一个中心点
  3. 在画时针,分针,秒针
  4. 想办法这三个针动起来
  5. 初始化对应时间,赋值这三个针上

表盘

这里采用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小工具。

相关推荐
Kapaseker14 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
Kapaseker2 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish2 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker3 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker4 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z5 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton6 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream6 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam6 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker7 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin