原标题:抖音上炫酷的网红文字时钟

本文作者

作者:二娃_

https://juejin.im/post/5cb53e93e51d456e55623b07

1

起源

源码地址

https://github.com/drawf/SourceSet/blob/master/app/src/main/java/me/erwa/sourceset/view/TextClockView.kt

周末在家刷抖音的时候看到了这款网红时钟,都是Android平台的,想来何不自己实现一把。看抖音里大家发的视频,这款时钟基本分两类,一类是展示在「壁纸」上,一类是展示在「锁屏」上。

展示到「壁纸」通过LiveWallPaper相关API可以做到,这也是本专题要实现的方式。

展示到「锁屏」目测是使用各ROM厂商的相关API,开发锁屏主题可以做到。

然而实现两者的基础便是拿起Canvas Paint等把它绘制出来,所以「上篇」我先用自定View的方式把时钟画出来,在Activity中展示效果。「下篇」的时候再把该View结合LiveWallPaper设置到壁纸。

2

思考分析

这是我当时截图下来的参考,先分析下涉及到的元素及样式表现:

「圆中信息」圆中心的数字时间+数字日期+文字星期几,始终为白色

「时圈」一圈文字小时,一点、二点..十二点,当前点数为白色,其它为白色+透明度,如图中十点就是白色。

「分圈」一圈文字分钟,一分、二分..五十九分,六十分显示为空,同理,当前分钟为白色,其它白色+透明度。

「秒圈」一圈文字秒,一秒、二秒..五十九秒,六十秒显示为空,也是同理。

然后分析下动画效果:

每秒钟「秒圈」走一下,这一下的旋转角度为360°/60=6°,并且走这一下的时候有个线性旋转过去的动画效果。

每分钟「分圈」走一下,旋转角度和动画效果跟「秒圈」相同。

每小时「时圈」走一下,旋转角度为360°/12=30°,动画效果同上。

3

绘制静态图

1. 画布准备

基本是将画布背景填充黑色,然后将画布的原点移动到View大小的中心,这样方便思维理解与绘制。

//在onLayout方法中计算View去除padding后的宽高

overridefunonLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int){

super.onLayout(changed, left, top, right, bottom)

mWidth = (measuredWidth - paddingLeft - paddingRight).toFloat

mHeight = (measuredHeight - paddingTop - paddingBottom).toFloat

//后文会涉及到

//统一用View宽度*系数来处理大小,这样可以联动适配样式

mHourR = mWidth * 0.143f

mMinuteR = mWidth * 0.35f

mSecondR = mWidth * 0.35f

}

//在onDraw方法将画布原点平移到中心位置

overridefunonDraw(canvas: Canvas?){

super.onDraw(canvas)

if(canvas == null) return

canvas.drawColor(Color.BLACK) //填充背景

canvas.save

canvas.translate(mWidth / 2, mHeight / 2) //原点移动到中心

//绘制各元件,后文会涉及到

drawCenterInfo(canvas)

drawHour(canvas, mHourDeg)

drawMinute(canvas, mMinuteDeg)

drawSecond(canvas, mSecondDeg)

//从原点处向右画一条辅助线,之后要处理文字与x轴的对齐问题,稍后再说

canvas.drawLine( 0f, 0f, mWidth, 0f, mHelperPaint)

canvas.restore

}

2. 画「圆中信息」

经过第一步,可以在AS的Xml Preview中看到一屏黑色+一条从屏幕中心到右边界的红线。(一眼望去,还是挺美的)

/**

* 绘制圆中信息

*/

privatefundrawCenterInfo(canvas: Canvas){

Calendar.getInstance.run {

//绘制数字时间

valhour = get(Calendar.HOUR_OF_DAY)

valminute = get(Calendar.MINUTE)

mPaint.textSize = mHourR * 0.4f //字体大小根据「时圈」半径来计算

mPaint.alpha = 255

mPaint.textAlign = Paint.Align.CENTER

canvas.drawText( "$hour:$minute", 0f, mPaint.getBottomedY, mPaint)

//绘制月份、星期

valmonth = ( this. get(Calendar.MONTH) + 1).let {

if(it < 10) "0$it"else"$it"

}

valday = this. get(Calendar.DAY_OF_MONTH)

valdayOfWeek = ( get(Calendar.DAY_OF_WEEK) - 1).toText //私有的扩展方法,将Int数字转换为 一、十一、二十等,后文绘制三个文字圈都会用该方法

mPaint.textSize = mHourR * 0.16f //字体大小根据「时圈」半径来计算

mPaint.alpha = 255

mPaint.textAlign = Paint.Align.CENTER

canvas.drawText( "$month.$day星期$dayOfWeek", 0f, mPaint.getTopedY, mPaint)

}

}

/**

* 扩展获取绘制文字时在x轴上 垂直居中的y坐标

*/

privatefunPaint.getCenteredY: Float{

returnthis.fontSpacing / 2- this.fontMetrics.bottom

}

/**

* 扩展获取绘制文字时在x轴上 贴紧x轴的上边缘的y坐标

*/

privatefunPaint.getBottomedY: Float{

return- this.fontMetrics.bottom

}

/**

* 扩展获取绘制文字时在x轴上 贴近x轴的下边缘的y坐标

*/

privatefunPaint.getToppedY: Float{

return- this.fontMetrics.ascent

}

其中要说一下mPaint.getBottomedY mPaint.getToppedY,这是两个扩展到Paint画笔上的两个kotlin方法。

他们的作用是为了处理绘制文字时与x轴的对齐关系。canvas.drawText方法的第三个参数是y坐标,但这个指的是文字的Baseline的y坐标,所以写了工具方法来得到矫正后的y坐标。(这里就只抛出这个点吧,具体实现原理可先查阅Paint类的相关API就会明白,文末会贴出我拜读的文章链接)

拿绘制数字时间举例,展示下不同效果:

把mPaint.getBottomedY替换成0f(y坐标为0,就是文字的Baseline坐标为0),文字使用15:67 abc jqk,可以看到两者区别。(红线就是前文画的那条好美的辅助线)

canvas.drawText("15 :67测试文字 abcjqk", 0 f, 0 f, mPaint)

canvas.drawText("15 :67测试文字 abcjqk", 0 f, mPaint.getBottomedY, mPaint)

ok,「圆中信息」绘制后长这个样子:

3. 画「时圈」「分圈」「秒圈」

绘制思路就是for循环12次,每次将画布旋转30°乘以i,然后在指定位置绘制文字,12次后刚好一个圆圈。

该方法接收一个degrees: Float参数,是控制「时圈」整体的旋转的,后文就是不断改变该值,而产生动画效果的。并且因为三个圈的动画方向都是逆时针,所以这个degrees是个始终会是个负数。

/**

* 绘制小时

*/

privatefundrawHour(canvas: Canvas, degrees: Float){

mPaint.textSize = mHourR * 0.16f

//处理整体旋转

canvas.save

canvas.rotate(degrees)

for(i in0until 12) {

canvas.save

//从x轴开始旋转,每30°绘制一下「几点」,12次就画完了「时圈」

valiDeg = 360/ 12f * i

canvas.rotate(iDeg)

//这里处理当前时间点的透明度,因为degrees控制整体逆时针旋转

//iDeg控制绘制时顺时针,所以两者和为0时,刚好在x正半轴上,也就是起始绘制位置。

mPaint.alpha = if(iDeg + degrees == 0f) 255else( 0.6f * 255).toInt

mPaint.textAlign = Paint.Align.LEFT

canvas.drawText( "${(i + 1).toText}点", mHourR, mPaint.getCenteredY, mPaint)

canvas.restore

}

canvas.restore

}

同理绘制「分圈」「秒圈」

/**

* 绘制分钟

*/

privatefundrawMinute(canvas: Canvas, degrees: Float){

mPaint.textSize = mHourR * 0.16f

//处理整体旋转

canvas.save

canvas.rotate(degrees)

for(i in0until 60) {

canvas.save

valiDeg = 360/ 60f * i

canvas.rotate(iDeg)

mPaint.alpha = if(iDeg + degrees == 0f) 255else( 0.6f * 255).toInt

mPaint.textAlign = Paint.Align.RIGHT

if(i < 59) {

canvas.drawText( "${(i + 1).toText}分", mMinuteR, mPaint.getCenteredY, mPaint)

}

canvas.restore

}

canvas.restore

}

/**

* 绘制秒

*/

privatefundrawSecond(canvas: Canvas, degrees: Float){

mPaint.textSize = mHourR * 0.16f

//处理整体旋转

canvas.save

canvas.rotate(degrees)

for(i in0until 60) {

canvas.save

valiDeg = 360/ 60f * i

canvas.rotate(iDeg)

mPaint.alpha = if(iDeg + degrees == 0f) 255else( 0.6f * 255).toInt

mPaint.textAlign = Paint.Align.LEFT

if(i < 59) {

canvas.drawText( "${(i + 1).toText}秒", mSecondR, mPaint.getCenteredY, mPaint)

}

canvas.restore

}

canvas.restore

}

DuangDuang!!效果出来啦~

4. 让时钟转起来

那么如何可以让时钟转起来呢?我们再看一下onDraw中的代码,绘制三个圈的方法都会接收一个相应的degrees: Float参数,这个是控制一个圈的整体旋转的,而且要逆时针转,所以始终得是负数。

这样一来就好说了,只要控制这三个角度变化,就能让时钟动起来。

overridefunonDraw(canvas: Canvas?){

super.onDraw(canvas)

... //省略

//绘制各元件,后文会涉及到

drawCenterInfo(canvas)

drawHour(canvas, mHourDeg)

drawMinute(canvas, mMinuteDeg)

drawSecond(canvas, mSecondDeg)

... //省略

}

那么首先定义三个角度的全局变量,并把他们与实际的时间关联起来,然后每隔一秒触发一次View的重绘即可。

//定义三个角度的全局变量

privatevarmHourDeg: FloatbyDelegates.notNull

privatevarmMinuteDeg: FloatbyDelegates.notNull

privatevarmSecondDeg: FloatbyDelegates.notNull

/**

* 绘制方法

*/

fundoInvalidate{

Calendar.getInstance.run {

valhour = get(Calendar.HOUR)

valminute = get(Calendar.MINUTE)

valsecond = get(Calendar.SECOND)

//这里将三个角度与实际时间关联起来,当前几点几分几秒,就把相应的圈逆时针旋转多少

mHourDeg = -360/ 12f * (hour - 1)

mMinuteDeg = -360/ 60f * (minute - 1)

mSecondDeg = -360/ 60f * (second - 1)

invalidate

}

}

然后只需在Activity中使用timer每秒钟刷新一次View即可。效果如下图,会发现转是转起来的,但是却每秒一跳。再看一下咱们当时的分析:

每秒钟「秒圈」走一下,这一下的旋转角度为360°/60=6°,并且走这一下的时候有个线性旋转过去的动画效果。

所以是还差一个线性旋转的效果。

//Activity中的代码

privatevarmTimer: Timer? = null

privatefuncaseTextClock{

setContentView(R.layout.activity_stage_text_clock)

mTimer = timer(period = 1000) {

runOnUiThread {

stage_textClock.doInvalidate

}

}

}

overridefunonDestroy{

super.onDestroy

mTimer?.cancel

}

5. 让时钟转的优雅点

基于我们已经知道了,时钟动起来的本质就是在一段时间内(比如150ms)不断的改变参数degrees: Float的值并触发重绘方法,这样就产生了人眼看到的动画效果。

所以,我们想让「秒圈」(三个圈的代表)转的更线性更优雅一点,就可以在要开始绘制新的一秒的时候,在前150ms线性的旋转6°。

init {

//处理动画,声明全局的处理器

mAnimator = ValueAnimator.ofFloat( 6f, 0f) //由6降到1

mAnimator.duration = 150

mAnimator.interpolator = LinearInterpolator //插值器设为线性

doInvalidate

}

/**

* 开始绘制

*/

fundoInvalidate{

Calendar.getInstance.run {

valhour = get(Calendar.HOUR)

valminute = get(Calendar.MINUTE)

valsecond = get(Calendar.SECOND)

mHourDeg = -360/ 12f * (hour - 1)

mMinuteDeg = -360/ 60f * (minute - 1)

mSecondDeg = -360/ 60f * (second - 1)

//记录当前角度,然后让秒圈线性的旋转6°

valhd = mHourDeg

valmd = mMinuteDeg

valsd = mSecondDeg

//处理动画

mAnimator.removeAllUpdateListeners //需要移除先前的监听

mAnimator.addUpdateListener {

valav = (it.animatedValue asFloat)

if(minute == 0&& second == 0) {

mHourDeg = hd + av * 5//时圈旋转角度是分秒的5倍,线性的旋转30°

}

if(second == 0) {

mMinuteDeg = md + av //线性的旋转6°

}

mSecondDeg = sd + av //线性的旋转6°

invalidate

}

mAnimator.start

}

}

就用这美丽优雅的时钟结尾吧~

文末

个人能力有限,如有不正之处欢迎大家批评指出,我会虚心接受并第一时间修改,以不误导大家。

https://github.com/drawf/SourceSet/blob/master/app/src/main/java/me/erwa/sourceset/view/TextClockView.kt返回搜狐,查看更多

责任编辑:

html时钟自动刷新抖音,抖音上炫酷的网红文字时钟相关推荐

  1. 抖音上炫酷的网红文字时钟

    看了鸿洋推荐的一位兄弟的这个时钟,感觉很帅,因为他是用kt写的,这里我把它改写成了java的,写了一下,也算跟着作者走一下流程与思路. 我这里直接上代码了,流程与思路请参看 原作者的文章 TextCl ...

  2. 抖音文字时钟壁纸html源码,这次要把抖音网红文字时钟设置为壁纸了~

    原标题:这次要把抖音网红文字时钟设置为壁纸了~ 本文作者 作者:二娃_ https://juejin.im/post/5d52aea86fb9a06ae61aad5b 还记得上篇吗?我们先实现了抖音网 ...

  3. 【自定义View】抖音网红文字时钟-上篇

    源码地址 抖音网红文字时钟-TextClockView 起源 周末在家刷抖音的时候看到了这款网红时钟,都是Android平台的,想来何不自己实现一把.看抖音里大家发的视频,这款时钟基本分两类,一类是展 ...

  4. 抖音文字时钟壁纸html,网红文字时钟怎么弄 抖音网红文字时钟主题壁纸设置教程...

    类型:桌面主题大小:54KB语言:中文 评分:10.0 标签: 立即下载 最近很多小伙伴在抖音上看到了网红文字时钟主题壁纸,很有意思,自己也想要制作这种的,网红文字时钟怎么弄,西西小编为大家带来抖音网 ...

  5. android 时钟字体下载,网红文字时钟轮盘手机版下载-网红文字时钟work clock安卓版下载 v1.0_5577安卓网...

    网红文字时钟work clock最新手机版提供,一款能够实现桌面时钟的手机壁纸.锁屏类软件,通过软件你可以让自己的手机桌面变成趣味的文字时钟哦,最近抖音和网红都在使用的壁纸APP,你也来下载使用吧! ...

  6. 动态壁纸安卓_网红文字时钟手机版来了!安卓手机和苹果手机都可以使用!

    今天,我们分享一下最近网上非常火的网红文字时钟手机设置方法.上次,我们为大家分享了Word Clock文字时钟电脑屏保,刚推送就有小伙伴留言要手机版的,不负所望,手机版终于来了! 首先我们看看效果图! ...

  7. 网红文字时钟手机版来了!安卓手机和苹果手机都可以使用!

    今天,我们分享一下最近网上非常火的网红文字时钟手机设置方法.上次,我们为大家分享了Word Clock文字时钟电脑屏保,刚推送就有小伙伴留言要手机版的,不负所望,手机版终于来了! 首先我们看看效果图! ...

  8. python 角度判断_python的turtle也能仿抖音网红文字时钟的代码及分析

    1.说明: 1.1 推荐指数:★★★ 1.2 推荐环境:python3.8和微软vscode编辑器 1.3 感言: python的turtle也能画出文字时钟,效果虽然不理想,有闪跳,但是也很棒,未采 ...

  9. 类似抖音平台是如何存储粉丝与网红之间人物关系的?

    作者:狄国良 [研究课题] 1.类似抖音平台是如何存储粉丝与网红之间人物关系的? 2.如何快速(最优)查询找到某网红与粉丝关系数据? 基于此课题思考相关性问题如下: 存储模型与场景 1.海量数据存储  ...

  10. c语言网红时钟代码源码,网红文本时钟PC版

    .版本 2 .支持库 iext2 .程序集 窗口程序集_启动窗口 .程序集变量 集_小时, 整数型, , "0" .程序集变量 集_分钟, 整数型, , "0" ...

最新文章

  1. vue 导航栏刷新页面定位:
  2. Oracle获取最近执行的SQL语句
  3. [实战]HM-Router configuration for TP ROOM
  4. Filter与Servlet区别
  5. java oracle rs.next_使用jsp连接oracle时,rs.next()值始终为false,表中存在数据
  6. Oracle vs MySQL
  7. dvwa如何打开_DVWA详细 安装
  8. 二十八、PHP框架Laravel学习笔记——模型的关联查询
  9. python结构模式图_NetworkX:Python图与网络模型基础
  10. python实现简易动态贝叶斯网络的推断
  11. 改变CSS世界纵横规则的writing-mode属性
  12. 雷达图像与电子海图叠加坐标变化优化方法
  13. Mugeda(木疙瘩)H5案例课—模拟朋友圈-岑远科-专题视频课程
  14. word整个表格首行缩进_word表格文字首行缩进
  15. 为知笔记使用自建docker服务后,如何迁移原有账号数据
  16. webstrom 2099年设置
  17. Flink:watermark
  18. 切片 (python)
  19. LTS = Long Term Support:长期支持版本
  20. html中写meta会乱码,网页html代码不可缺少的5个meta标签属性

热门文章

  1. jsp员工管理系统mysql_基于JSP的企业员工信息管理系统的设计(MySQL)
  2. FileUtils工具类学习
  3. 慎用 JSON.stringify
  4. 苹果手机长截屏_智能长截屏工具,安卓/ios/电脑全平台都给你
  5. jquery.nicescroll.min.js滚动条插件的用法
  6. 怎么直接播放或编辑WEBM格式的视频
  7. 宏杉MacroSan存储开局指导-流程
  8. PostgreSQL 存储过程(Procedures)和函数(Functions)的区别
  9. java程序员 英文简历_it程序员英文简历范文模板
  10. ktv服务器管理系统,小型KTV综合解决方案