展示时间的方式一般要么是用钟表形式,要么是用数字形式,我在之前的文章中已经用过Compose实现了一个钟表的时钟,但是数字时钟还没有做过,主要是实现个数字时钟太容易,调用个时间api获取当前时间然后渲染到一个文本组件上,功能就完成了,没啥好做的,但如果在这简易的功能上做一些扩展,比如在切换时间的时候加个粒子动效的过度,比如使用像素字体来展现数字,比如让它能常驻在桌面上充当一个真正的时钟角色,那么这个数字时钟就看起来不那么单调了,下面就开始来看看如何实现这个时钟
粒子动效
整个过程比较关键的就是实现粒子动效,由于水平有限没法将这个效果做的太酷炫,只能先实现个简易版的,也就是随机在一个范围内绘制若干个粒子,首先创建个函数ParticleClock
,在内部添加一个Canvas
以及获取这个Canvas
的大小
当画布创建出来后,就可以通过mSize
来获取画布的宽高,接着再创建个变量count
来表示单次绘制的粒子数量,然后创建个pointList
用来保存单次需要绘制的所有粒子
SingleParticle
封装着每一个粒子的xy坐标
生成一组粒子的操作也就是循环count
次,每一次新建个SingleParticle
,里面的xy坐标取宽高的随机值,代码如下
创建好了粒子之后,就要开始绘制了,由于我们的数字时钟采用像素字体,而每一个像素其实就是一个小的矩形,所以我们的粒子也用矩形来绘制,这样当粒子动效结束过度到数字时钟的时候就不会显的很违和,绘制代码如下
现在界面上就有五十个随机绘制的小矩形
这里稍作优化,现在每个粒子的宽高都是固定的大小,但是我们希望粒子的大小可以随着窗口的变化,粒子数量的变化而改变,所以这里需要将每个矩形的宽高更改为mSize.width/count
和mSize.height/count
,更改后的代码以及效果图如下
现在绘制的粒子都是静态的,如果想让粒子可以动态绘制的话,就必须通过重组每次新建一个pointList
并塞入新的一组粒子,这样每次绘制粒子的位置就都不一样了,为此这里需要创建一个状态变量index
,然后每单位时间更改一下这个值
这里使用LaunchedEffect
定时更改index
值,这样index
更新后,pointList
内部的粒子也刷新了,我们看一下效果
更改粒子的绘制高度
大概的一个粒子动效就有了,现在需要再粒子动画的过程中逐渐改变粒子的绘制高度,直到高度的变化值达到一个定值的时候再将时钟展示出来,这个变化值就由index
记录着,而定值就设置为mSize.height/3
,这样当上下高度都缩小mSize.height/3
后,中间剩余的mSize.height/3
高度就可以用来展示时间,我们在LaunchedEffect
里面更改一下代码
这里当index
的值到达mSize.height/3
之后,将index
的值重置为0,这样变小的绘制范围又重新恢复到原样,此外由于这边的粒子绘制高度发生了变化,在给pointList
赋值的时候,每一个SingleParticle
的y值的取值范围也要对应的改变,代码如下
这里除了rangeY
的取值范围与index
绑定在了一起之外,rangeX
的取值也变成了50与mSize.width.toInt() - 50
之间,让粒子的绘制范围与两边留有一些空隙,那么现在粒子的绘制范围可以跟着index
的变化而改变了,效果如下
显示时间
粒子的动效做的差不多了,现在要开始将时钟展现出来,要达到的效果就是当index
值达到mSize.height/3
的时候,隐藏粒子,展示时钟,那么这里需要一个状态值来控制这两个视图的隐藏展示
当index
值达到mSize.height/3
的时候showNumber
设置为true
,并且延迟个一段时间再将showNumber
变成false
,那么这个延迟的时间就是时钟的展示时间,一样在LaunchedEffect
中加入这些逻辑
那么现在就开始时钟的绘制,由于我们的时钟的文字使用的是像素字体,这个在之前的一篇文章中有讲过如何制作像素字体的数字,所以这里直接将那边的代码拷了一份过来,在创建像素字体的函数中添加对时间中间的冒号符号的支持
再创建一个函数ClockItem
用做创建时钟上的时分秒,也就是判断下传进来的数字的位数,然后决定使用一个DigitalNumber
或者是两个DigitalNumber
来展示
最后在创建个表示时钟的函数ClockDigital
,除了接受些必要的参数如Modifier
之外,还接收当前时间对象LocalTime.now()
,内部分别时候三个ClockItem
代表时分秒,两个DigitalNumber
表示冒号
时钟部分的代码就是这些,我们将函数ClockDigital
与绘制粒子的Canvas
都包在一个Box
组件内部,用变量showNumber
来控制展示哪个组件
整体的一个逻辑代码就完成了,来看看效果如何
可以很容易的发现个bug,也就是同一个时间它出现了两次,造成这个bug的原因主要是因为没有将粒子的动画时间控制好,理论上来说由于时间的展示时间为500毫秒,那么粒子的动画时间也应该是500毫秒,然后这个500毫秒也包含了创建数据以及渲染数据的时间,所以index
从0变成大于mSize.height/3
要比500毫秒小,经过调试最终index
每十毫秒的增值如下所示
看下效果
窗口透明
最后一步了,现在整个数字时钟也完成了,我们需要让它摆放在桌面上某一个位置让它在那边静静地跳,要做的就是去掉边框,让窗口透明,代码如下
transparent
与undecorated
属性是让窗口透明,alwaysOnTop
是可选项,目的是让窗口永远浮在其他窗口上面,一直可见,在运行一下项目,在桌面的底部就出现了我们的数字时钟了
总结
一个带粒子动画和像素字体的数字时钟完成了,多多少少以后这个也能在桌面上当作一个时钟工具了,或者到了中午将它整成全屏直接作为一个屏保,让它在那边跳,然后你去吃饭也是可以的,这个大家有兴趣的可以自己拿去调一下样式,比如外围加个边框,或者多一些颜色搭配,这个就看个人喜好了~