最近在学Compose跨平台的内容,在上一篇文章中,已经用Compose DeskTop实现了一个秒表应用,那既然能实现秒表,那么一样的我们也可以实现时钟的应用,这一篇文章中我们就来看看如何用Compose DeskTop实现一个时钟应用
开始开发
首先我们还是先找到Main.kt文件里面main
函数,并在Window
里面创建一个Timeclock
函数作为我们时钟的入口
在TimeClock
函数里面,首先创建两个变量screenW
跟screenY
,分别表示画布的宽高,并且在Canvas
里面对这俩变量赋值,表示当画布的大小发生变化的时候,更新screenW
与screenY
的值,触发TimeClock
重组
有了画布的宽高,接下来就是要找到钟表的中心点位置,一般情况下我们把画布的中心当作钟表的中心点,所以它的x,y坐标分别是
然后由于钟表是个圆,所以我们还需要一个半径,半径的大小决定了这个钟表在画布当中所占的大小,所以半径的最大值是circleX
与circleY
的最小值
半径减去100的原因是不希望钟表画出来太贴边,最好能够有些许间距,当然这是个人审美,因人而异,钟表的范围已确定,接下来准备画里面的刻度,刻度这里分整点刻度与非整点刻度两部分,整点刻度用十二个数字表示,分别分布在圆周上,每经过三十度一个点,所以这里我们需要两个数组,分别存储整点的文案以及整点的角度
另外还创建了一个TextMeasurer
,用来绘制文案的时候使用,有了以上这些变量,我们现在可以在Canvas
里面先把整点文案绘制出来了
pointX
和pointY
分别是计算圆周上x坐标与y坐标的函数,计算出来以后分别在减去40主要是因为drawText
是从topLeft
开始绘制文案,如果不减去点偏移量,文案会显的比较靠下,pointX
和pointY
函数的代码如下
现在我们看一下效果,整点的文案已经出来了
整点文案绘制完毕,然后是非整点刻度的绘制,首先是确定好非整点刻度的角度,那就是除了圆周上整点刻度角度之外的其余角度,所以我们也创建个数组来保存这些非整点刻度的角度
然后这边希望非整点刻度是一根根小的分割线,每一根分割线都指向了钟表圆心,那么需要用到的绘制函数已经知道了,就是drawLine
,然后drawLine
里面start与end的坐标就通过pointX
与pointY
函数计算出来,start坐标离圆心近点,那么第一个入参就设定为半径长度减10,end坐标离圆心远一些,那么入参就定为半径加10,代码如下
现在所有刻度都绘制完毕,效果如下
看起来有点别扭是不,10点,11点,12点看起来还好,其他的感觉有点位置偏左,想了一想也是,个位数与两位数偏移量怎么可能设置的相同呢,所以我们在drawText
那里增加个位数的判断,如果是个位数,那么偏移量就比两位数的小点,代码如下
现在我们再看下效果
看起来好点了,接下来就开始绘制秒针,分针和时针,在绘制之前,我们首先想一下如何绘制,要知道这不仅仅是画三根line那样简单,每一秒三根指针在钟表上的位置都有可能变化,也就是说这是一个周期性的事件,说到周期性的事件,实现方式有很多种,在传统Android里面,我们经常使用TimeTask
,线程池,JobScheduler
等来实现,但这些方案都要经过生成实例来实现,而我们Composable函数是要经常发生重组的,一旦发生重组就又新建个实例,那就太消耗内存了,所以在Compose里面,我更倾向于使用Flow
,因为Flow
的数据流需要在一个协程作用域里面进行,而LaunchedEffect
函数恰好可以在Composable函数里面提供这样一个协程作用域,另外,LaunchedEffect
函数只有在key值发生变化的时候才会触发协程作用域重新执行里面的代码,如果key值不变,哪怕发生重组也不用担心重新创建Flow
,现在我们就来创建这个时间的数据流
先是创建了一个LocalTime
的实例time
,并且用remember
监听它的变化,我们在LaunchedEffect
函数里面创建了一个Flow
,并在上游一秒一次持续发送最新时间,在下游对time
进行赋值,所以我们变量time
永远都表示着最新时间,然后我们创建三个变量分别将time
的时分秒赋值进去
我们先绘制秒针,秒针基本每秒都会改变个位置,它经过一分钟以后走的刻度数量刚好是60个,这60个刻度所对应的角度我们创建个list保存起来
这样我们每次绘制line的时候,对应的角度在totalList
数组里面的下标值就是当前second
的时间,绘制秒针的代码如下
秒针绘制完毕,我们看下效果如何
分针与秒针的原理基本相同,通过变量minute
就可以在totalList
中获取分针绘制的角度,我们只需要在样式上稍作修改就好,绘制分针的代码如下
运行以后的效果如下
分针也绘制完毕了,并且我们看到当秒针经过刻度12的时候,分针也往后走了一格,功能也没问题,最后我们来绘制时针,时针稍微复杂一点,因为它跟秒针分针不一样,变量hour
在一个小时以内都是保持不变的,但是时针的位置不能在一个小时以内都指在整点刻度上,它需要在在一小时内从当前整点刻度走到下一个整点刻度,所以它经过的角度我们需要将hour
进行适当的计算来获取,计算的代码如下所示
减去12是因为LocalTime
如果不做处理,直接获取的时间是24小时制的,所以需要判断如果到了下午就要减去12,下一步乘上5是将对应整点换算成刻度,然后再加上如果是非整点,那么在两个刻度之间所经过的角度,这样计算下来,我们也可以通过hour
在totalList
中获取时针的绘制角度,绘制的代码基本相同,样式上稍作修改
三个指针都绘制完毕,稍作优化,在圆心的地方再绘制个小圆,小圆的颜色同画布背景色一致,达到的效果就像是指针的重合部位是空心的一样,我们看下效果
两个打包小经验
查看执行文件的log
这个主要是因为刚开始接触DeskTop开发,有些东西还比较陌生,比如打出来的包如果运行失败了,如何知道是哪里出了问题?这个在Android里面非常容易,插上线在logcat中看下日志就可以了,那么桌面程序如果出错了,日志在哪里看呢?反正Intelliji里面我找了下没找到,具体log位置是在右键打包出来的程序,然后在Contents/MacOS里面有个可执行文件,双击执行一下,它的运行日志就会在弹出来的终端里面展示出来
打出来的包运行失败
这个是上一个问题衍生出来的,因为打包失败了所以我得找是啥原因啊,写客户端最终打不了包那不是白玩吗,首先看一下是什么日志
java.lang.UnsatisfiedLinkError: Can’t load library: /Applications/TimeClock.app/Contents/app/libskiko-macos-arm64.dylib
主要是这个报错,产生这个报错的文件是在Library.kt文件里面
网上查了不少,有说把compose.desktop.currentOs改成compose.desktop.macos_x64,但是这边试了下没啥用,然后看到有人说升级一下kotlin跟Compose版本,将kotlin升级到1.7.20,Compose升级到1.2.2,但由于我当前的配置比这个推荐的版本还要高所以没有在意,我的配置如下所示
结果又尝试了几次既然失败以后,我打算把版本降级到网上推荐的那个版本,配置文件改为如下所示
想着如果再不行就用家里的另一台windows试试看了,当然Compose版本降到1.3.0以下代码里是要做一些改动的,那就是要把drawText相关代码注释掉,因为这个函数是1.3.0之后推出的,最终执行了packageDmg命令后,文件真的执行成功了,效果图如下所示
我们看到虽然整点的刻度没有了,但是我们的包的确运行成功了,至于为什么高版本的kotlin跟Compose打包出来的包运行不成功,这个还得继续找找原因。
总结
这个demo算是比较简单,但好在知道了如何将一个DeskTop程序打包成可执行文件了,不然之前包打不成还真是蛮头疼的,后面继续Compose Multiplatform的学习与小demo开发