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小工具。

相关推荐
传奇开心果编程1 小时前
【Jetpack Compose基础语法学与练】第8课 rememberSaveable,页面旋转/系统重建保留状态
android·学习·ui·kotlin·android jetpack
传奇开心果编程5 小时前
【Jetpack Compose基础语法学与练】第7课 if条件渲染 + 列表基础(LazyColumn)
学习·ui·kotlin·android jetpack
ai2work5 小时前
ch12 系统能力接入:TTS、剪贴板、分享、文件选择器
kotlin
传奇开心果编程6 小时前
【Jetpack Compose基础语法学与练】第9课 SideEffect副作用 + LaunchedEffect,处理异步任务和生命周期
android·学习·ui·kotlin·android jetpack
不要喷香水1 天前
00-总览与架构
kotlin·app·档案管理
hai_android1 天前
Android 组件化开发实践
android·java·kotlin
传奇开心果编程2 天前
【Jetpack Compose基础语法学与练】第6课 TextField文本输入,字符串状态与输入交互
学习·前端框架·kotlin·android jetpack
ai2work2 天前
ch06 Jetpack Compose 入门:状态驱动 UI
kotlin
JMchen3 天前
第 12 篇|项目整合与打包发布 —— 从 Demo 到可安装 APK 的完整收官指南
kotlin·android studio
Android打工仔3 天前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin