Android实现简单时钟View的方法
作者:swt369 发布时间:2022-10-21 18:36:00
标签:Android,时钟,View
通过Canvas的平移与旋转简化绘图逻辑是一个非常有用的技巧,下面的时钟view就是利用这个方法完成的,省去了使用三角函数计算坐标的麻烦。
package com.example.swt369.simpleclock;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Toast;
import java.util.Calendar;
/**
* Created by swt369 on 2017/8/20.
*/
public class ClockView extends View {
//时钟各个部件的长度
private float mScaleLengthLong;
private float mScaleLengthShort;
private float mTickLengthHour;
private float mTickLengthMinute;
private float mTickLengthSecond;
//view的宽和高
private float mWidth;
private float mHeight;
//时钟半径
private float mRadius;
//绘制各部件时用的Paint
private Paint mPaintScaleLong;
private Paint mPaintScaleShort;
private Paint mPaintOutline;
private Paint mPaintNum;
private Paint mPaintTickHour;
private Paint mPaintTickMinute;
private Paint mPaintTickSecond;
private ViewTreeObserver.OnPreDrawListener onPreDrawListener;
public ClockView(final Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
//获取view宽高并计算各个部件的长度
@Override
public boolean onPreDraw() {
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
mRadius = Math.min(mWidth,mHeight) / 2 * 0.95f;
mScaleLengthLong = mRadius * 0.1f;
mScaleLengthShort = mRadius * 0.05f;
mTickLengthHour = mRadius * 0.3f;
mTickLengthMinute = mRadius * 0.45f;
mTickLengthSecond = mRadius * 0.6f;
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(onPreDrawListener);
//点击显示具体时间
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Calendar calendar = Calendar.getInstance();
String time = String.format("当前时间:%02d:%02d:%02d",
calendar.get(Calendar.HOUR_OF_DAY),calendar.get(Calendar.MINUTE),calendar.get(Calendar.SECOND));
Toast.makeText(context,time, Toast.LENGTH_SHORT).show();
}
});
//初始化所有Paint对象
initializePaints();
}
@Override
protected void onDraw(Canvas canvas) {
if(mWidth == 0 || mHeight == 0){
return;
}
if(onPreDrawListener != null){
getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener);
onPreDrawListener = null;
}
//绘制时钟
drawClock(canvas);
//一秒后重绘
postInvalidateDelayed(1000);
}
private void drawClock(Canvas canvas) {
//保存原始状态
canvas.save();
//将坐标系原点移到中心,并逆时针旋转90度。完成后x轴朝上。
canvas.translate(mWidth / 2,mHeight / 2);
canvas.rotate(-90);
//画外围轮廓
canvas.drawCircle(0,0,mRadius, mPaintOutline);
//画刻度
for(int i = 0 ; i < 12 ; i++){
String num = String.valueOf(i == 0 ? 12 : i);
if(i % 3 == 0){
canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong);
}else {
canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort);
}
canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum);
//顺时针旋转30度
canvas.rotate(30);
}
Calendar calendar = Calendar.getInstance();
//画时针
int hour = calendar.get(Calendar.HOUR);
canvas.save();
canvas.rotate(hour * 30);
canvas.drawLine(0,0,mTickLengthHour,0,mPaintTickHour);
canvas.restore();
//画分针
int minute = calendar.get(Calendar.MINUTE);
canvas.save();
canvas.rotate(minute * 6);
canvas.drawLine(0,0,mTickLengthMinute,0,mPaintTickMinute);
canvas.restore();
//画秒针
int second = calendar.get(Calendar.SECOND);
canvas.save();
canvas.rotate(second * 6);
canvas.drawLine(0,0,mTickLengthSecond,0,mPaintTickSecond);
canvas.restore();
//恢复原始状态
canvas.restore();
}
private void initializePaints(){
mPaintScaleLong = new Paint();
mPaintScaleLong.setAntiAlias(true);
mPaintScaleLong.setStrokeWidth(5);
mPaintScaleShort = new Paint();
mPaintScaleShort.setAntiAlias(true);
mPaintScaleShort.setStrokeWidth(3);
mPaintOutline = new Paint();
mPaintOutline.setStyle(Paint.Style.STROKE);
mPaintOutline.setAntiAlias(true);
mPaintOutline.setStrokeWidth(5);
mPaintNum = new Paint();
mPaintNum.setTextSize(30);
mPaintTickHour = new Paint();
mPaintTickHour.setAntiAlias(true);
mPaintTickHour.setStrokeWidth(6);
mPaintTickMinute = new Paint();
mPaintTickMinute.setAntiAlias(true);
mPaintTickMinute.setStrokeWidth(4);
mPaintTickSecond = new Paint();
mPaintTickSecond.setAntiAlias(true);
mPaintTickSecond.setStrokeWidth(2);
}
}
代码注释已经比较详细了,下面把比较重要的部分再说明一下:
//保存原始状态
canvas.save();
//省略中间部分...
//恢复原始状态
canvas.restore();
在绘图开始时调用canvas.save(),可以保存下未经任何平移、旋转操作的原始画布状态。在所有绘图工作完成后调用canvas.restore(),可以恢复到上一次保存的状态(类似进出栈的感觉)。
//将坐标系原点移到中心,并逆时针旋转90度。完成后x轴朝上。
canvas.translate(mWidth / 2,mHeight / 2);
canvas.rotate(-90);
画布的平移与旋转可能比较抽象,可以想象成坐标系的平移与旋转。调用canvas.translate(dx,dy)相当于将坐标原点向x,y方向移动了dx,dy的距离,调用canvas.rotate(degree)相当于坐标系顺时针旋转了degree°。
//画刻度
for(int i = 0 ; i < 12 ; i++){
String num = String.valueOf(i == 0 ? 12 : i);
if(i % 3 == 0){
canvas.drawLine(mRadius,0,mRadius - mScaleLengthLong,0, mPaintScaleLong);
}else {
canvas.drawLine(mRadius,0,mRadius - mScaleLengthShort,0, mPaintScaleShort);
}
canvas.drawText(num,mRadius - mScaleLengthLong - mPaintNum.measureText(num) * 2,0, mPaintNum);
//顺时针旋转30度
canvas.rotate(30);
}
根据i值决定是画大刻度(90°的倍数)还是小刻度。每次画完一条之后将坐标系顺时针旋转30°,这样保证每次画的刻度线的坐标不变。
//一秒后重绘
postInvalidateDelayed(1000);
作用是在1000ms(1s)后再次执行View的draw流程,产生指针运动的效果。
来源:http://blog.csdn.net/swt369/article/details/77429532


猜你喜欢
- 题主要区分清楚内码(internal encoding)和外码(external encoding)就好了。内码是程序内部使用的字符编码,特
- 做tcp网络编程,要解析一批批的数据,可是数据是通过Socket连接的InputStream一次次读取的,读取到的不是需要转换的对象,而是要
- 这篇文章主要介绍了Java GZIP压缩与解压缩代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 存储的可选项Android 的文件系统类似于其他平台的基于磁盘的文件系统。包括以下几种存储类别:App 私有的存储空间共享的存储空间Pref
- 本文实例为大家分享了SpringMVC框架实现图片上传与下载的具体代码,供大家参考,具体内容如下1、新建一个Maven webapp项目,引
- 利用Java连接MySQL做登陆界面,供大家参考,具体内容如下1、首先需要建立一个类,在这里,我命名为newLoginnewLogin类的代
- Class类中获取方法:public Method[] getMethods();//获取包括自身和继承(实现)过来的所有的public方法
- 实现手机电话状态的监听,主要依靠两个类:TelephoneManger和PhoneStateListener。 TelephonseMang
- 这篇文章主要介绍了Java如何把数组转换为ArrayList,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 请停止代码注释“干净的代码应该像写好的散文一样” - Robert C. Martin不良代码的通病就是有很多注释。这是凌乱的源代码最明显的
- SpringBoot现在基本上都是使用application-XXX.yml(生产环境为pro,开发测试环境为dev)来配置项目中的一些配置
- 项目结构:效果图一:效果图二:效果图三:关于闹钟:Alarm hongten v1.0 使用说明: &nbs
- 首先说一下,教科书上的扫描线算法确实是用c++很好实现,而且网上有很多源码,而java实现的基本没有(可能是我没看到),所以肖先生还是打算自
- 线程的同步是保证多线程安全访问竞争资源的一种手段。线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同
- 这篇文章主要介绍了java通过Jsoup爬取网页过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- JAVA操作XML文档主要有四种方式,分别是DOM、SAX、JDOM和DOM4J,DOM和SAX是官方提供的,而JDOM和DOM4J则是引用
- mapper文件使用in("str1","str2")mybatis的xxxMapper.xml文件
- 本文实例讲述了Android编程设计模式之迭代器模式。分享给大家供大家参考,具体如下:一、介绍迭代器模式(Iterator Pattern)
- public interface ICacheStrategy { &
- 如果不熟悉Java8新特性的小伙伴,初次看到函数式接口写出的代码可能会是一种懵逼的状态,我是谁,我在哪,我可能学了假的Java,(・∀・(・