博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/smile_running

偶然间看到了一个时钟罗盘的动画效果,那个是桌面版的,用来当屏保效果还不错。于是呢,在抖音视频上搜了一下,果然找到这种时钟的效果视频,当然还有设置的教程。至于什么效果,插一段抖音视频的动态图:

就是这个样子的,由于它这个视频格式是 mp4 的,也无法上传,就录了一点点效果,也可以看了。

首先呢,看到这个效果,感觉还是可以的,正好博主这几天都在搞自定义 View 这一块,恰好也有这个兴致可以玩一玩。之前还没做过类似于时钟的效果,刚好可以尝试一下。

于是呢,我就开始盯着这个动画看了好一会儿,把里面的一些信息给记录了下来。首先呢,它是以罗盘的形式在转动的,可以观察它的罗盘指针,那个高亮文本的信息指出的就是当前的系统时间,而且它是始终固定在那里的。

罗盘呢,是一个联动效果的仪器,从最外圈带动内圈转动,起到更新时间的效果。但这些都是我们的视觉效果,其实不就是绘制一个一个圆,计算好它们的半径,然后圆上面都是文字嘛。

经过了上面的初步分析,然后我就开始起手写代码了。我刚开始也是照着视频中的效果还原的,不过很可惜,这个视频中的信息量太大了,由于我们的手机屏幕比较小,不太适合视频中的那么多信息,于是我就把其中的月份、星期等给去除了,我们剩下的就是这样的效果:

细心的小伙伴可能一眼就发现,你这个效果明显和视频里面的有差距,视频里面有旋转动画,这个没有啊。这个确实,我个人能力有限,在代码中也添加了旋转动画效果,可能计算动画时,会有一个 bug,目前呢,还没有得到改善,还望大佬们指点指点。

不过呢,实现这个效果,才是我们的首要目的,动画什么的只是锦上添花。接下来,我们来看看实现的步骤和要点吧。

首先呢,我们从最里面的 12 个时辰开始,这里需要获取一下系统的时间,然后取匹配我们的对应的字符,因为系统的默认格式是:01~12 这样的,显然我们需要中文的格式,但这部分也比较简单。

接着我们需要把文字绘制成一圈的形式,重点开始。如何绘制一圈的文字,我在这也卡了挺久的,我的做法是这样的,首先把画布的中心点平移到屏幕的中心,这个好说。然后 12 个时辰绘制一圈,就是 360°/12 吧,这个也好说。但是呢,这里我们不能直接进行绘制,那会出现这个效果:

文本是水平的,但是效果中是有偏移角度的。于是呢,我就想到用 canvas 的 rotate 方法,没绘制一个文本,旋转 360°/12 的角度即可,因为有 12 个时辰,只需要来个循环就搞定了。

    private void drawHour(Canvas canvas) {float perAngle = 360f / 12f;int minuteIndex = Integer.valueOf(getTime("hh")) - 1;String[] preString = Arrays.copyOfRange(mHour, 0, minuteIndex);String[] sufString = Arrays.copyOfRange(mHour, minuteIndex, 12);String[] newHour = concat(sufString, preString);for (int i = 0; i < 12; i++) {canvas.save();//设置当前画笔颜色float curAngle = perAngle * i;setCurrentColor(curAngle);//镜像效果canvas.scale(-1, 1, 0, 0);//旋转画布canvas.rotate(curAngle, 0, 0);mPaints[1].setTextScaleX(-1);canvas.drawText(newHour[i], -180, 0 + mTextHeight, mPaints[1]);canvas.restore();}}

就是上面的代码,旋转了画布。不过呢,这里旋转画布之后,我们的起始位置是在左边的,就是那个高亮的文本会在左边位置,而且文字是倒过来的,所以要对画布进行 scale 镜像处理,让高亮文本移动右边,并且文字为正常显示。

除了这个细节的处理,还有一个是 paint 笔的处理,默认的话,画布被我们镜像了之后,会出现这样的情况,文本的 “十点” 变成倒过来了 “点十”,并且呢它是向内的,这就有点难受了。不过还好,paint 也有提供镜像的功能,我们上面的代码,也对 paint 进行了镜像操作,顺利解决诸多问题,终于把一 到十二点给绘制成了一圈的样式了。

接下来就是 1 ~ 59 分和1 ~ 59 秒了呗,这就与 1~12 时辰一个方法,只不过要主要的是,它们都有 60 个,是从 00 ~ 59 的,所以每一度要用 360°/60 才行,并且半径要算好,刚刚好留点小间距,别让文字重合即可。

    private void drawMinute(Canvas canvas) {float perAngle = 360f / 60f;int minuteIndex = Integer.valueOf(getTime("mm"));String[] preString = Arrays.copyOfRange(mMinute, 0, minuteIndex);String[] sufString = Arrays.copyOfRange(mMinute, minuteIndex, 60);String[] newMinute = concat(sufString, preString);for (int i = 0; i < 60; i++) {canvas.save();//设置当前画笔颜色float curAngle = perAngle * i;setCurrentColor(curAngle);//镜像效果canvas.scale(-1, 1, 0, 0);//旋转画布canvas.rotate(curAngle, 0, 0);mPaints[1].setTextScaleX(-1);canvas.drawText(newMinute[i], -getBound().width() * 6f - 120, 0 + mTextHeight, mPaints[1]);canvas.restore();}}

上面的是绘制分钟的代码,绘制小时的我就不贴出来了,后面会贴完整代码。接着就是中心部分的时间了,这部分没上面好说的,就是计算坐标,绘制文本,代码如下:

    private void drawCenterTime(Canvas canvas) {String time = getTime("HH:mm:ss");mPaints[0].setColor(Color.WHITE);mPaints[0].setTextSize(70f);Rect bounds = new Rect();mPaints[0].getTextBounds(time, 0, time.length(), bounds);Paint.FontMetrics fontMetrics = mPaints[0].getFontMetrics();float y = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.descent;canvas.drawText(time, -bounds.width() / 2, y, mPaints[0]);}

接下来就是动画了,我们就每 1 秒获取系统时间,然后刷新一次 View,就完成了。

    private void setTimeAndAnimator() {if (timeAnimator == null) {timeAnimator = ObjectAnimator.ofFloat(0f, -6f);timeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {diff = (float) animation.getAnimatedValue();
//                    invalidate();}});timeAnimator.setDuration(1000);timeAnimator.start();timeAnimator.setInterpolator(new LinearInterpolator());timeAnimator.setRepeatCount(-1);timeAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationRepeat(Animator animation) {invalidate();}});}}

这里的动画监听,如上面注释的那行刷新代码,它是会开启动画效果的,但是有点细节没有处理好,不知到如何计算坐标了,动画不是特别流畅,所以我给它屏蔽了。

好了,下面是完整的代码:

package nd.no.xww.qqmessagedragview;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;/*** @author xww* @desciption : 抖音视频里的一个时钟罗盘效果* @date 2019/8/10* @time 14:48* 博主:威威喵* 博客:https://blog.csdn.net/smile_Running*/
public class DYClockCompass extends View {/*** 1、当前时间的获取,简单* 2、当前时间的颜色(判断是否当前时间)* 3、绘制刻度,罗盘指针固定位置,变动的只有刻度* <p>* 4、刻度信息,由内到外:月份、号数、周数、小时、分钟、秒*/private String[] mHour = new String[]{"一点", "二点", "三点", "四点", "五点", "六点", "七点", "八点", "九点", "十点", "十一点", "十二点"};private String[] mMinute = new String[]{"零分", "一分", "二分", "三分", "四分", "五分", "六分", "七分", "八分", "九分", "十分","十一分", "十二分", "十三分", "十四分", "十五分", "十六分", "十七分", "十八分", "十九分", "二十分","二十一分", "二十二分", "二十三分", "二十四分", "二十五分", "二十六分", "二十七分", "二十八分", "二十九分", "三十分","三十一分", "三十二分", "三十三分", "三十四分", "三十五分", "三十六分", "三十七分", "三十八分", "三十九分", "四十分","四十一分", "四十二分", "四十三分", "四十四分", "四十五分", "四十六分", "四十七分", "四十八分", "四十九分", "五十分","五十一分", "五十二分", "五十三分", "五十四分", "五十五分", "五十六分", "五十七分", "五十八分", "五十九分"};private String[] mSeconds = new String[]{"零秒", "一秒", "二秒", "三秒", "四秒", "五秒", "六秒", "七秒", "八秒", "九秒", "十秒","十一秒", "十二秒", "十三秒", "十四秒", "十五秒", "十六秒", "十七秒", "十八秒", "十九秒", "二十秒","二十一秒", "二十二秒", "二十三秒", "二十四秒", "二十五秒", "二十六秒", "二十七秒", "二十八秒", "二十九秒", "三十秒","三十一秒", "三十二秒", "三十三秒", "三十四秒", "三十五秒", "三十六秒", "三十七秒", "三十八秒", "三十九秒", "四十秒","四十一秒", "四十二秒", "四十三秒", "四十四秒", "四十五秒", "四十六秒", "四十七秒", "四十八秒", "四十九秒", "五十秒","五十一秒", "五十二秒", "五十三秒", "五十四秒", "五十五秒", "五十六秒", "五十七秒", "五十八秒", "五十九秒"};private int mWidth;private int mHeight;private float mCenterX;private float mCenterY;private Paint[] mPaints = new Paint[2];private float mTextHeight;private Timer timer = new Timer();private void init() {mPaints[0] = getPaint(Color.BLACK);mPaints[1] = getPaint(Color.GRAY);mPaints[1].setStyle(Paint.Style.FILL);Paint.FontMetrics fontMetrics = mPaints[1].getFontMetrics();mTextHeight = Math.abs((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.descent);}private Paint getPaint(int color) {Paint paint = new Paint();paint.setDither(true);paint.setAntiAlias(true);paint.setTextSize(30f);paint.setColor(color);return paint;}public DYClockCompass(Context context) {this(context, null);}public DYClockCompass(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public DYClockCompass(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mWidth = MeasureSpec.getSize(widthMeasureSpec);mHeight = MeasureSpec.getSize(heightMeasureSpec);mCenterX = mWidth / 2;mCenterY = mHeight / 2;}@Overrideprotected void onDraw(Canvas canvas) {canvas.drawColor(Color.BLACK);canvas.translate(mCenterX, mCenterY);
//        canvas.drawLine(0, 0, mWidth / 2, 0, mPaints[2]);drawHour(canvas);drawMinute(canvas);drawSeconds(canvas);setTimeAndAnimator();drawCenterTime(canvas);}public Rect getBound() {Rect rect = new Rect();mPaints[1].getTextBounds("一", 0, "一".length(), rect);return rect;}@SuppressLint("SimpleDateFormat")private String getTime(String format) {return new SimpleDateFormat(format).format(new Date(System.currentTimeMillis()));}private void drawHour(Canvas canvas) {float perAngle = 360f / 12f;int minuteIndex = Integer.valueOf(getTime("hh")) - 1;String[] preString = Arrays.copyOfRange(mHour, 0, minuteIndex);String[] sufString = Arrays.copyOfRange(mHour, minuteIndex, 12);String[] newHour = concat(sufString, preString);for (int i = 0; i < 12; i++) {canvas.save();//设置当前画笔颜色float curAngle = perAngle * i;setCurrentColor(curAngle);//镜像效果canvas.scale(-1, 1, 0, 0);//旋转画布canvas.rotate(curAngle, 0, 0);mPaints[1].setTextScaleX(-1);canvas.drawText(newHour[i], -180, 0 + mTextHeight, mPaints[1]);canvas.restore();}}private void drawMinute(Canvas canvas) {float perAngle = 360f / 60f;int minuteIndex = Integer.valueOf(getTime("mm"));String[] preString = Arrays.copyOfRange(mMinute, 0, minuteIndex);String[] sufString = Arrays.copyOfRange(mMinute, minuteIndex, 60);String[] newMinute = concat(sufString, preString);for (int i = 0; i < 60; i++) {canvas.save();//设置当前画笔颜色float curAngle = perAngle * i;setCurrentColor(curAngle);//镜像效果canvas.scale(-1, 1, 0, 0);//旋转画布canvas.rotate(curAngle, 0, 0);mPaints[1].setTextScaleX(-1);canvas.drawText(newMinute[i], -getBound().width() * 6f - 120, 0 + mTextHeight, mPaints[1]);canvas.restore();}}static String[] concat(String[] a, String[] b) {String[] c = new String[a.length + b.length];System.arraycopy(a, 0, c, 0, a.length);System.arraycopy(b, 0, c, a.length, b.length);return c;}private void drawSeconds(Canvas canvas) {float perAngle = 360f / 60f;int secondsIndex = Integer.valueOf(getTime("ss"));String[] preString = Arrays.copyOfRange(mSeconds, 0, secondsIndex);String[] sufString = Arrays.copyOfRange(mSeconds, secondsIndex, 60);String[] newSeconds = concat(sufString, preString);
//        Log.i("========", "newSeconds: " + Arrays.toString(newSeconds));for (int i = 0; i < 60; i++) {canvas.save();//镜像效果canvas.scale(-1, 1, 0, 0);//设置当前画笔颜色float curAngle = perAngle * i;setCurrentColor(curAngle);//旋转画布canvas.rotate(curAngle + diff, 0, 0);mPaints[1].setTextScaleX(-1);canvas.drawText(newSeconds[i], -getBound().width() * 11f - 120, 0 + mTextHeight, mPaints[1]);canvas.restore();}}ValueAnimator timeAnimator = null;private float diff;private void setTimeAndAnimator() {if (timeAnimator == null) {timeAnimator = ObjectAnimator.ofFloat(0f, -6f);timeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {diff = (float) animation.getAnimatedValue();
//                    invalidate();}});timeAnimator.setDuration(1000);timeAnimator.start();timeAnimator.setInterpolator(new LinearInterpolator());timeAnimator.setRepeatCount(-1);timeAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationRepeat(Animator animation) {invalidate();}});}}private void drawCenterTime(Canvas canvas) {String time = getTime("HH:mm:ss");mPaints[0].setColor(Color.WHITE);mPaints[0].setTextSize(70f);Rect bounds = new Rect();mPaints[0].getTextBounds(time, 0, time.length(), bounds);Paint.FontMetrics fontMetrics = mPaints[0].getFontMetrics();float y = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.descent;canvas.drawText(time, -bounds.width() / 2, y, mPaints[0]);}private void setCurrentColor(float curAngle) {if (curAngle == 0)mPaints[1].setColor(Color.WHITE);elsemPaints[1].setColor(Color.GRAY);}}

最后,这个效果仅仅是我写来玩一玩的,偶然看到的一个时钟罗盘的软件,然后自己瞎写的,并没有处理分别率的问题,我的模拟器是 1920 * 1080 的,我是按这样的分辨率写的,在不同的分辨率可能会有不同的效果,还请自己修改参数。

最后的最后,是这个动画的问题,这个没有完成的动画始终有点放不下,如果大佬有兴趣可以去进行修改一下动画的代码,达到那个视频的效果,可以多多交流一下。

自定义 View 之抖音时钟罗盘仪效果相关推荐

  1. Android 抖音爱心动画,Android自定义View实现抖音飘动红心效果

    本文实例为大家分享了Android自定义View实现抖音飘动红心效果的具体代码,供大家参考,具体内容如下 自定义View--抖音飘动红心 效果展示 动画效果 使用自定义view完成红心飘动效果 Vie ...

  2. android 自定义红心,Android自定义View实现抖音飘动红心效果

    本文实例为大家分享了Android自定义View实现抖音飘动红心效果的具体代码,供大家参考,具体内容如下 自定义View--抖音飘动红心 效果展示 动画效果 使用自定义view完成红心飘动效果 Vie ...

  3. 自定义View:仿抖音直播点赞效果

    目录 一.效果图 1.第一版本:在屏幕底部开始显示 2.第二版本:点击任意位置都可以显示 3.第三版本:给任意控件添加点赞效果 二.代码 1.第一版本代码 源码: 示例: 2.第二版本代码 源码(主要 ...

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

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

  5. 自定义View实现车载音场设置效果

    本文阅读大约十分钟 此文章来源于星友的提问,关于自定义View一个车机上的音场效果设置,星球提问是免费的,感谢老哥赞赏的鸡腿~,写文章不易,分析原理到实现效果也是要花费一些时间和精力,不过大家加入星球 ...

  6. Android仿抖音双击点赞动画,Android仿抖音点击效果

    原标题:Android仿抖音点击效果 作者丨wish_xy https://www.jianshu.com/p/1d17c38a3db1 学习自定义view,想找点东西耍一下,刚好看到抖音的点赞效果不 ...

  7. Android仿抖音我的页面,Android自定义videoview仿抖音界面

    本文实例为大家分享了Android自定义videoview仿抖音界面的具体代码,供大家参考,具体内容如下 1.效果图 和抖音的界面效果一模一样,而且可以自定义,需要什么页面,请自己定义 2.自定义vi ...

  8. 微信小程序实现抖音切换视频效果

    微信小程序实现抖音切换视频效果 思路: 使用微信小程序的swiper组件,使其竖向滑动 分页加载,每次加载3个视频,当滑动到只剩1个视频时加载下一页 问题: 加载多个视频时,多个视频会同时播放 效果图 ...

  9. Android 利用ConstraintLayout 实现仿抖音点赞动画效果

    正好在做一个和抖音差不多的APP,目前在刚启动阶段,先从实现一个抖音的点赞动画开始...爱心是从阿里的矢量图标库下载的一个爱心的Png图片,不是使用贝塞尔曲线画的...原因是我不会贝塞尔曲线(其实就是 ...

  10. 仿抖音短视频源码,高仿抖音双击点赞效果之双击的问题

    仿抖音短视频源码中,实现仿抖音的双击点赞效果,相关代码如下: public class MyView extends View {private GestureDetector gestureDete ...

最新文章

  1. jquery学习之1.20-获取同辈元素和子元素
  2. sharepoint Lists Web service 用法
  3. 排序算法 —— 计数排序
  4. 用css3实现Social Media Buttons
  5. JVM专题之垃圾回收器
  6. 5不能另存为dwg_5.建立数模
  7. JavaScript原型、函数伪装(apply,call)、继承
  8. mysql事务隔离的锁_mysql锁及四种事务隔离级别笔记
  9. 阿里云环境迁移记录 - RabbitMQ集群搭建
  10. 启动3Dmax到初始化..界面卡住然后闪退解决办法
  11. 闪迪tf卡量产工具_Tony哥的自修室:相机SD卡摔碎角,钣金喷漆技术活复原一波...
  12. 台式计算机时间跳动,电脑时间总是自动跳
  13. Java户籍管理系统的设计与实现
  14. es远程主机强迫关闭了一个现有的连接
  15. 人工智能在材料科学的应用
  16. OfficeExcel(1)
  17. android执行lua脚本的方法,android执行lua脚本的方法
  18. 每天一剂 WebView 良药
  19. python替换列表中元素_python中关于元素替换的一些总结
  20. Unity学习之PostProcessing的使用

热门文章

  1. java性能调优_Java性能调优常用手段
  2. 记录一次CenterOS7中xmrig挖矿病毒的排查测试
  3. python培训 首选马哥教育
  4. 苹果手机左上角的数字怎么弄_一键更改手机IP操作方法
  5. 步进电机基础(7.2)-步进电机的选择方法-位置定位精度、转速方面、转速变化率和依据使用环境来选择
  6. PSPNet: Pyramid Scene Parsing Network论文解读
  7. 华为手表表盘的数字什么意思_华为gt2表盘上的数字是什么意思
  8. numpy安装与调试
  9. html编写代码制作网站教程,html代码编写教程
  10. 密度聚类算法python详解_密度聚类python