小米的UI相对来说是做的很美观的,这里就简单的模仿一下小米的时钟,上面部分效果没有加上,先看下效果图:
下面是小米的UI:
去实现这个效果其实只需要分为三个步骤就可以了:
1、画外围的圆弧、数字
2、画刻度线
3、画时针、分针、秒针
整个View的一些属性,如颜色、背景等等可以使用自定义属性进行设置增加其灵活性,这里为了简单就直接在代码中设置了。这里只是大致的思路,没有贴出全部代码。基本的工作就不做多的介绍,这里创建一个类ClockView继承View,初始化各个Paint、设置属性、各个坐标的变量等等。
1、画外围的圆弧、数字
首先定义一个RectF确定圆弧的区域:
// 设置圆弧绘制区域
float left = (float) (clockX - 1.2 * radius);// 根据原点的中心左边和刻度线的半径
float top = (float) (clockY - 1.2 * radius);
float right = (float) (clockX + 1.2 * radius);
float bottom = (float) (clockY + 1.2 * radius);
RectF rect = new RectF(left,top,right,bottom);
这里的难点是文字的绘制,文字其实也是一块矩形区域,它的锚点在左下角,所以绘制文字时为了在弧线的中间要注意锚点的坐标,以右边的“3”为例,绿线是圆弧区域的右边界,它的X坐标是已经定义好的–right,那现在只需要知道“3”文字的宽度textWidth,拿right减去textWidth/2正好就是锚点的X坐标。在水平方向也是居中的,那只需要用圆心的y坐标clockY加上文字高度的一半,文字高度是自己设定的,参考下面的示意图,剩下的“6”、“9”、“12”以此类推:
private void drawArcLines(Canvas canvas) {
// 设置圆弧绘制区域
float left = (float) (clockX - 1.2 * radius);// 根据原点的中心左边和刻度线的半径
float top = (float) (clockY - 1.2 * radius);
float right = (float) (clockX + 1.2 * radius);
float bottom = (float) (clockY + 1.2 * radius);
RectF rect = new RectF(left,top,right,bottom);
// 圆弧设置
arcPaint.setStyle(Paint.Style.STROKE);
arcPaint.setStrokeWidth(1);
arcPaint.setARGB(125, 255, 255, 255);
// 字体设置
textPaint.setTextSize(40);
textPaint.setARGB(125, 255, 255, 255);
String num = "0";
// 文字的坐标
float textX = 0;
float textY = 0;
float textWidth = 0;
// 通过循环,每90度绘制以此圆弧和文字
for (int i = 0; i < 360; i += 90) {
// 画圆弧,注意圆弧的起始终止弧度,留一定的间隙给文字
canvas.drawArc(rect, 5 + i, 80, false, arcPaint);
// 计算文字锚点坐标
switch (i) {
case 0:
num = "3";
textWidth = textPaint.measureText(num);
textX = right - textWidth / 2;
textY = clockY + 10;
break;
case 90:
num = "6";
textWidth = textPaint.measureText(num);
textX = clockX - textWidth / 2;
textY = bottom + 10;
break;
case 180:
num = "9";
textWidth = textPaint.measureText(num);
textX = left - textWidth / 2;
textY = clockY + 10;
break;
case 270:
num = "12";
textWidth = textPaint.measureText(num);
textX = clockX - textWidth / 2;
textY = top + 10;
break;
}
// 绘制文字
canvas.drawText(num, textX, textY, textPaint);
}
}
2、画刻度线
画刻度线相对来说比较简单,知道刻度线的其实终止点坐标,例如下面的示意图,起始点a的想x、y坐标可以利用绿色圆形的半径乘以对应的三角函数,绿色圆形的半径radius可以直接根据屏幕的大小设定,这里不做解释了,终止点的坐标那就是粉色圆形的半径乘以三角函数,粉色圆形半径则可以设置成radius的百分之多少。计算的结果必须加上相应圆形的x或y坐标,这样才是真正相对于界面的坐标值。
知道怎么画一条之后,这一圈的刻度线就可以利用循环绘制,画多少条则根据每一条间隔的角度可以设定。
// 画短线
private void drawShortLines(Canvas canvas) {
paint.setStrokeWidth(4);
for (double i = 0; i < 2 * PI; i += PI / 60) {
float startX = (float) (radius * Math.cos(i)) + clockX;
float startY = (float) (radius * Math.sin(i)) + clockY;
float endX = (float) (0.85 * radius * Math.cos(i)) + clockX;
float endY = (float) (0.85 * radius * Math.sin(i)) + clockY;
paint.setARGB(125, 255, 255, 255);
canvas.drawLine(startX, startY, endX, endY, paint);
}
}
3、画时针、分针、秒针
画这几个指针的思路是一样的,通过路径Path进行绘制
1、秒针
用 Path 绘制一个指向 12点钟 的三角形,通过不断旋转画布实现秒针的旋转,三个点的坐标需要自己去调整:
private void drawSecPath(Canvas canvas){
secPaint.setARGB(255,255,255,255);
canvas.save();
canvas.rotate(secDregee,clockX,clockY);// 旋转该层画布
float offset = radius * 0.15f;
secPath.moveTo(clockX, clockY - 0.8f * radius);
secPath.lineTo(clockX + offset / 2,clockY - 0.8f * radius + 0.86f * offset);
secPath.lineTo(clockX - offset / 2,clockY - 0.8f * radius + 0.86f * offset);
secPath.close();
canvas.drawPath(secPath,secPaint);
canvas.restore();
}
2、分针
分针可以看做是一个梯形,同样用路径Path绘制:
private void drawMinHand(Canvas canvas){
minPaint.setARGB(200,255,255,255);
canvas.save();
canvas.rotate(minDregee,clockX,clockY);
minPath.moveTo(clockX - 0.02f * radius,clockY - 0.1f * radius);
minPath.lineTo(clockX - 0.01f * radius,clockY - 0.7f * radius);
minPath.lineTo(clockX + 0.01f * radius,clockY - 0.7f * radius);
minPath.lineTo(clockX + 0.02f * radius,clockY - 0.1f * radius);
minPath.close();
canvas.drawPath(minPath,minPaint);
canvas.restore();
}
2、时针
时针也可以看做是一个梯形,同样用路径Path绘制:
private void drawHourHand(Canvas canvas){
hourPaint.setARGB(255,255,255,255);
canvas.save();
canvas.rotate(hourDregee,clockX,clockY);
hourPath.moveTo(clockX - 0.03f * radius,clockY - 0.06f * radius);
hourPath.lineTo(clockX - 0.02f * radius,clockY - 0.6f * radius);
hourPath.lineTo(clockX + 0.02f * radius,clockY - 0.6f * radius);
hourPath.lineTo(clockX + 0.03f * radius,clockY - 0.06f * radius);
hourPath.close();
canvas.drawPath(hourPath,hourPaint);
}
在中间还有一个圆环,直接画一个空心圆并设置一些线条的宽度即可:
private void drawCoverCircel(Canvas canvas) {
circlePaint.setColor(Color.WHITE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeWidth(0.06f * radius);
canvas.drawArc(clockX - 0.08f * radius,clockY - 0.08f * radius,
clockX + 0.08f * radius,clockY + 0.08f * radius,
0f,360f,false,circlePaint);
}
终效果:
到这里所有的图形已绘制完毕,现在则需要让时分秒针动起来,在这三个绘制的方法中都有一个旋转的画布的方法,其中第一个参数就是指定画布旋转的角度,只要不断的改变其参数值就可让时分秒针动起来,这里在定义一个方法,将系统时间换算成时分秒针对应的旋转角度:
private void getTime(){
Calendar calendar = Calendar.getInstance();
float milliSecond = calendar.get(Calendar.MILLISECOND);
// 在时分秒后面加上分、秒、毫秒,可以让指针旋转更加平滑,减少顿挫感
float second = calendar.get(Calendar.SECOND) + milliSecond / 1000;
float min = calendar.get(Calendar.MINUTE) + second / 60;
float hour = calendar.get(Calendar.HOUR) + min / 60;
// 角度换算
secDregee = second / 60 * 360 ;
minDregee = min / 60 * 360;
hourDregee = hour / 12 * 360;
}
终的onDraw方法如下:
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
canvas.drawARGB(255, 40, 120, 200);
getTime();
drawArcLines(canvas);
drawShortLines(canvas);
drawSecPath(canvas);
drawMinHand(canvas);
drawHourHand(canvas);
drawCoverCircel(canvas);
// 在主线程调用界面刷新,刷新时调用onDraw方法,循环往复
invalidate();
}
好了,到这里整个流程已经结束,现在这个ClockView就可以在布局文件中直接使用了:
<LinearLayout xmlns:android="//schemas.android.com/apk/res/android"
xmlns:tools="//schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#123"
android:orientation="vertical"
tools:context="com.hqyj.clockview.MainActivity">
<com.hqyj.clockview.ClockView
android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/clockView" />
</LinearLayout>
热点新闻