安卓自定义View仿小米定时器实现代码

今天在写APP的时候遇到一个需求,要显示事件发生了多久了,首先想到的是如本站评论一样,弄个标签,显示多久多久前。然而老板并不是这个意思,要弄个表一样的东西,让它转动,我擦….简直坑爹,于是我就发挥我面向百度编程的技能,找到各种定时器、时钟的开源项目,比如仿华为时钟的,仿小米时钟的。还有我接下来介绍的安卓仿小米定时器的自定义view实现代码。效果很不错,是我需要的样子,大家看看效果吧。

安卓自定义View仿小米定时器实现代码

安卓自定义View仿小米定时器实现代码

废话不多说,直接看实现代码!

public class StopwatchView extends View {
private final Paint mTrianglePaint;//三角形画笔
private final Paint mLinePaint;//刻度线的画笔
private final Paint mTextPaint;//文字画笔
private final Paint mInnerCirclePaint;//内部圆形
private int mLen; //实际尺寸大小
private int mMilliseconds; //计时的总毫秒数
private int outerAngle;//外圆指针的角度
private int innerAngle;//小圆指针角度
private float mTriangleLen;
private boolean isPause;//是否暂停的开关
float eachLineAngle = 360f / 240f; //两个刻度线之间的角度1.5° 共240条线 240间隔
private Timer mTimer;   //定时器
private String mShowContent; //显示总的时间
public StopwatchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//三角形指针画笔
mTrianglePaint = new Paint();
mTrianglePaint.setColor(Color.WHITE);
mTrianglePaint.setAntiAlias(true); //抗锯齿
//刻度线的画笔
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setStrokeWidth(2); //设线宽
//文字画笔
mTextPaint = new Paint();
mTextPaint.setTextAlign(Paint.Align.CENTER); //文字居中
mTextPaint.setColor(Color.WHITE);
mTextPaint.setAntiAlias(true);
mTextPaint.setStrokeWidth(2);
//内部圆形画笔
mInnerCirclePaint = new Paint();
mInnerCirclePaint.setColor(Color.WHITE);
mInnerCirclePaint.setStyle(Paint.Style.STROKE); //无填充
mInnerCirclePaint.setAntiAlias(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//重新定义尺寸,保证为正方形
int width = measuredDimension(widthMeasureSpec);
int height = measuredDimension(heightMeasureSpec);
mLen = Math.min(width, height);
//小三角形指针端点到外圆之间的距离,用于计算三角形坐标[这里取整体宽度的1/16]
mTriangleLen = (float) mLen / 16.0f;
//提交设置新的值
setMeasuredDimension(mLen, mLen);
}
//适配不同尺寸
private int measuredDimension(int measureSpec) {
int defaultSize = 800; //默认大小
int mode = MeasureSpec.getMode(measureSpec); //宽高度设定方式
int size = MeasureSpec.getSize(measureSpec); //宽高度测量大小
switch (mode) {
case MeasureSpec.EXACTLY: //尺寸指定
return size;
case MeasureSpec.AT_MOST: //match_parent
return size;
case MeasureSpec.UNSPECIFIED: //wrap_content
return defaultSize;
default:
return defaultSize;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
calculateValue();
drawTriangle(canvas);
drawLine(canvas);
drawText(canvas);
drawSecondHand(canvas);
}
//计算相关值【根据当前毫秒值,计算外指针角度和内圆指针角度】
private void calculateValue() {
//文字
int hours = mMilliseconds / (1000 * 60 * 60);
int minutes = (mMilliseconds % (1000 * 60 * 60)) / (1000 * 60);
int seconds = (mMilliseconds - hours * (1000 * 60 * 60) - minutes * (1000 * 60)) / 1000;
int milliSec = mMilliseconds % 1000 / 100;
if (hours == 0) {
mShowContent = toDoubleDigit(minutes) + ":" + toDoubleDigit(seconds) + "." + milliSec;
} else {
mShowContent = toDoubleDigit(hours) + ":" + toDoubleDigit(minutes) + ":" + toDoubleDigit(seconds) + "." + milliSec;
}
//外角度
outerAngle = 360 * (mMilliseconds % 60000) / 60000;
//内角度
innerAngle = 360 * (mMilliseconds % 1000) / 1000;
}
//根据角度绘制内部秒针
private void drawSecondHand(Canvas canvas) {
canvas.save();
canvas.translate(mLen / 2, (float) mLen * 3 / 4.0f - mLen / 16);
canvas.drawCircle(0, 0, mLen / 12, mInnerCirclePaint);
canvas.drawCircle(0, 0, mLen / 80, mInnerCirclePaint);
canvas.rotate(innerAngle);
canvas.drawLine(0, mLen / 80, 0, mLen / 14, mInnerCirclePaint);
canvas.restore();
}
//绘制文字
private void drawText(Canvas canvas) {
canvas.save();
canvas.translate(mLen / 2, mLen / 2);
mTextPaint.setTextSize(mLen / 10);
canvas.drawText(mShowContent, 0, 0, mTextPaint);
canvas.restore();
}
//绘制外部刻度线
private void drawLine(Canvas canvas) {
canvas.save();
canvas.translate(mLen / 2, mLen / 2);
int totalLines = (int) (360f / eachLineAngle); //240条线
int lastLine = (int) (outerAngle / eachLineAngle);  //最亮的线条
int firstLine = lastLine - ((int) (90 / eachLineAngle)); //最暗的一条
boolean negativeFlag = false; //负数标志【即表示跨过了0起始坐标】
if (firstLine < 0) {
negativeFlag = true;
firstLine = totalLines - Math.abs(firstLine);
}
int count = 0;
for (int i = 0; i < totalLines; i++) {
canvas.rotate(eachLineAngle);
int color = 0;
if (!negativeFlag) {
//没有跨过起始点标志
if (i >= firstLine && i <= lastLine && count < (totalLines / 4)) {
count++;
color = Color.argb(255 - ((totalLines / 4 - count) * 3), 255, 255, 255);
} else {
color = Color.argb(255 - (int) (360f * 3 / (eachLineAngle * 4)), 255, 255, 255);
}
} else {
//跨过起始点
if (i >= 0 && i < lastLine) {
if (count == 0) {
count = totalLines / 4 - lastLine;
} else {
count++;
}
color = Color.argb(255 - ((totalLines / 4 - count) * 3), 255, 255, 255);
} else if (mMilliseconds != 0 && i < totalLines && i >= firstLine) {  //mMilliseconds!=0 条件限制,目的是初始化时 都是灰色线条
count++;
color = Color.argb(255 - ((totalLines / 4 - (i - firstLine)) * 3), 255, 255, 255);
} else {
color = Color.argb(255 - (int) (360f * 3 / (eachLineAngle * 4)), 255, 255, 255);
}
}
mLinePaint.setColor(color);
//mTriangleLen/5距离 目的是为了三角形到线条之间保留的距离
canvas.drawLine(0, (float) (mLen / 2 - (mTriangleLen + mTriangleLen / 5)), 0, (float) (mLen / 2 - (2 * mTriangleLen + mTriangleLen / 5)), mLinePaint);
}
canvas.restore();
}
//根据角度绘制三角形
private void drawTriangle(Canvas canvas) {
canvas.save();
//确定坐标
canvas.translate(mLen / 2, mLen / 2);
canvas.rotate(outerAngle);
//画三角形
Path p = new Path();
//指针点
p.moveTo(0, mLen / 2 - mTriangleLen);
//左右侧点
p.lineTo(0.5f * mTriangleLen, mLen / 2 - 0.134f * mTriangleLen);
p.lineTo(-0.5f * mTriangleLen, mLen / 2 - 0.134f * mTriangleLen);
p.close();
canvas.drawPath(p, mTrianglePaint);
canvas.restore();
}
//转成两位数 如传入1 返回01,
private String toDoubleDigit(int value) {
if (value < 10) {
return "0" + value;
} else {
return "" + value;
}
}
//开始
public void start() {
if (mTimer == null) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
if (!isPause) {
mMilliseconds += 50;
//工作线程中用postInvalidate(); UI线程用invalidate()
postInvalidate();
}
}
}, 50, 50);
} else {
resume();
}
}
//暂停
public void pause() {
isPause = true;
}
//继续
private void resume() {
isPause = false;
}
//重置
public void reset() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
isPause = false;
mMilliseconds = 0;
invalidate();
}
public  void setmMilliseconds(String datetime){
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
try {
Date date=simpleDateFormat.parse(datetime);
long startTime =fromDateStringToLong(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(new Date()));
long stopTime = fromDateStringToLong(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS").format(date));
this.mMilliseconds=(int)(startTime-stopTime);
} catch (ParseException e) {
e.printStackTrace();
}
}
//记录
public int record() {
return mMilliseconds;
}
/**
* 根据String型时间,获取long型时间,单位毫秒
* @param inVal 时间字符串
* @return long型时间
*/
public static long fromDateStringToLong(String inVal) {
Date date = null;
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
try {
date = inputFormat.parse(inVal);
} catch (Exception e) {
e.printStackTrace();
}
return date.getTime();
}
}

作者并没有提供设置已经过去多久的方法,是我自己添加的,效果还可以,没有什么问题,注意使用了毫秒级的日期时间格式化。

使用方法

新建一个java文件,将上面的代码保存下来,然后在布局文件中这样使用:

<com.daimadog.com.myview.StopwatchView
android:id="@+id/myclock"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

其中,com.daimadog.com.myview是我的安卓包名,也就是你新建java文件后出现在文件最上方的那一行package后面的字符串,使用时记得改成你自己的。

然后在activity中使用findViewById找到控件,根据你的需要使用start、stop、pause、resume、reset几个方法即可,另外上面我提供了一个初始化时间的方法,只需在start之前使用setmMilliseconds方法传入开始日期时间字符串即可!

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/242275.html

(0)
上一篇 2022年4月7日 17:33
下一篇 2022年4月7日 17:33

相关推荐

发表回复

登录后才能评论