之前实现过html5版的钟表,html5也有一个画板属性Canvas,相对于安卓的Canvas来说html5的功能要强大的多,就拿钟表的实现,html5要方便简单的多,而安卓实现起来则非常复杂,像指针转动,html5可以画一条线,然后可以用这条线旋转一个弧度即可,而安卓必须是已知起点坐标和终点坐标,这就需要精确计算起始坐标了,
安卓自定义View实现钟表
。先看下效果图,因为是图静态的,动态图也懒得做,所以看个大致效果即可,具体效果可以下载demo观看:
这个钟表并不算完美,正常的钟表,秒针每转动一格,分针和时针都会跟着转动相应的弧度,但本例中偷了个懒,只有秒针转一周时,分针才移动一小格,分针转一周时,时针移动一大格,不满一周的情况下不动,因为画线需要精确的坐标值,如果做到这种效果那工作量可就大了去了,因此我们知道原理即可。
实现思路:
1.重写View,并在ondraw方法中绘图;
2.定义边框宽度,刻度宽度,长度,指针宽度,长度,计算圆心坐标及表盘半径;
3.绘制边框:
mPaint.setStrokeWidth(bordWidth); // 设置圆环的宽度 mPaint.setColor(bordColor); canvas.drawCircle(getWidth()/2, getWidth()/2, r, mPaint);
4.绘制刻度:
//画刻度 //12 mPaint.setStrokeWidth(timeLineB_W); mPaint.setColor(timeColor); canvas.drawLine(circleX,bordWidth, circleX, bordWidth+timeLineB_H, mPaint); //12-1 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(90-i*6)) ),(float)(circleX-r*Math.cos(Math.toRadians(i*6))), (float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-i*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(i*6))),mPaint); } //1 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(60)) ),(float)(circleX-r*Math.cos(Math.toRadians(30))), (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60))),(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30))),mPaint); //1-2 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(90-30-i*6)) ),(float)(circleX-r*Math.cos(Math.toRadians(30+i*6))), (float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-i*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(30+i*6))),mPaint); } //2 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(30)) ),(float)(circleX-r*Math.cos(Math.toRadians(60))), (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30))),(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60))),mPaint); //2-3 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(90-60-i*6)) ),(float)(circleX-r*Math.cos(Math.toRadians(60+i*6))), (float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-i*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(60+i*6))),mPaint); } //3 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine(getWidth()-bordWidth, circleX, getWidth()-bordWidth-timeLineB_H, circleX, mPaint); //3-4 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(i*6)) ),(float)(circleX+r*Math.cos(Math.toRadians(90-i*6))), (float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(i*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-i*6))),mPaint); } //4 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(30)) ),(float)(circleX+r*Math.cos(Math.toRadians(60))), (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30))),(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60))),mPaint); //4-5 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(30+i*6)) ),(float)(circleX+r*Math.cos(Math.toRadians(90-30-i*6))), (float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(30+i*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-i*6))),mPaint); } //5 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(60)) ),(float)(circleX+r*Math.cos(Math.toRadians(30))), (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60))),(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30))),mPaint); //5-6 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX+r*Math.cos(Math.toRadians(60+i*6)) ),(float)(circleX+r*Math.cos(Math.toRadians(90-60-i*6))), (float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(60+i*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-i*6))),mPaint); } //6 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine(circleX,getWidth()-bordWidth, circleX, getWidth()-bordWidth-timeLineB_H,mPaint); //6-7 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(90-i*6)) ),(float)(circleX+r*Math.cos(Math.toRadians(i*6))), (float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-i*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(i*6))),mPaint); } //7 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(60)) ),(float)(circleX+r*Math.cos(Math.toRadians(30))), (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60))),(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30))),mPaint); //7-8 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(90-30-i*6)) ),(float)(circleX+r*Math.cos(Math.toRadians(30+i*6))), (float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-i*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(30+i*6))),mPaint); } //8 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(30)) ),(float)(circleX+r*Math.cos(Math.toRadians(60))), (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30))),(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60))),mPaint); //8-9 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(90-60-i*6)) ),(float)(circleX+r*Math.cos(Math.toRadians(60+i*6))), (float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-i*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(60+i*6))),mPaint); } //9 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine(bordWidth,circleX, bordWidth+timeLineB_H, circleX,mPaint); //9-10 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(i*6)) ),(float)(circleX-r*Math.cos(Math.toRadians(90-i*6))), (float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(i*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-i*6))),mPaint); } //10 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(30)) ),(float)(circleX-r*Math.cos(Math.toRadians(60))), (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30))),(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60))),mPaint); //10-11 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(30+i*6)) ),(float)(circleX-r*Math.cos(Math.toRadians(90-30-i*6))), (float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(30+i*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-i*6))),mPaint); } //11 mPaint.setStrokeWidth(timeLineB_W); canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(60)) ),(float)(circleX-r*Math.cos(Math.toRadians(30))), (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60))),(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30))),mPaint); //11-12 mPaint.setStrokeWidth(timeLineS_W); for(int i=1;i<6;i++){ canvas.drawLine((float)(circleX-r*Math.cos(Math.toRadians(60+i*6)) ),(float)(circleX-r*Math.cos(Math.toRadians(90-60-i*6))), (float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(60+i*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-i*6))),mPaint); }刻度是逐一计算起始点坐标并绘制的,拿1点的刻度来说,
已知圆心坐标(x,y),半径r,AB长度为a,求A点B点坐标。这应该是初中的题目了吧,很好计算,只要求出A点到y轴和x轴的距离,B点到y轴和x轴的距离即可(最方便的办法就是利用正余玄函数了,而Java也提供了正余玄函数的算法:Math.cos(Math.toRadians(角度值),因为Math.cos传的是弧度,所以要将角度转化为弧度如Math.toRadians(60)),当然要注意屏幕坐标与数学中的象限坐标是不同的,所以AB点坐标要用求出的距离值结合圆的半径求出该点在屏幕中的实际坐标,然后画线即可。
5.绘制指针交点:
mPaint.setStrokeWidth(2); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(circleX,circleX, 10, mPaint);6.秒针转动:
到这里,我们得思考下,钟表是要动的啊,那怎么实现打开应用钟表就开始走呢?很简单吧,初始化View时,在View的构造方法中开启个线程去不断重绘View即可,时间间隔当然为一秒,这也就给你视觉感是指针一秒转动一次,另外需要你对秒针分针时针转动时相互关联所走的角度有一个清晰的思路,如果你连他们之间的关系都搞不明白,那看代码就更不用说了:
while (true){ if (ss == 360){ mm++;//秒针每转一圈,分针走一格,一格6度,一圈60格 if(mm==60){ hh++;//分针转一圈,时针走一格,一格30度,一圈12格 if(hh==12){ hh=0; } mm=0; } ss = 0; } postInvalidate(); //重绘 try{ Thread.sleep(1000); //暂停1秒 } catch (InterruptedException e){ e.printStackTrace(); } ss+=6; //360度,分针每秒转动6度 }
秒针转动:
mPaint.setStrokeWidth(timeLineS_W-5);//设置秒针宽度 mPaint.setColor(pointColor); r=r-timeLineB_H-5;//设置秒针长度 //分针随秒针转动 if(ss<=30){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-ss*1))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(ss*1))), mPaint); }else if(ss<=60){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(ss-30)*1))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(30+(ss-30)*1))), mPaint); }else if(ss<=90){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(ss-60)*1))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(60+(ss-60)*1))), mPaint); }else if(ss<=120){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians((ss-90)*1))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-(ss-90)*1))), mPaint); }else if(ss<=150){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(30+(ss-120)*1))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(ss-120)*1))), mPaint); }else if(ss<=180){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(60+(ss-150)*1))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(ss-150)*1))), mPaint); }else if(ss<=210){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-(ss-180)*1))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians((ss-180)*1))), mPaint); }else if(ss<=240){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(ss-210)*1))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(30+(ss-210)*1))), mPaint); }else if(ss<=270){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(ss-240)*1))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(60+(ss-240)*1))), mPaint); }else if(ss<=300){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians((ss-270)*1))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-(ss-270)*1))),mPaint); }else if(ss<=330){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(30+(ss-300)*1))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(ss-300)*1))),mPaint); }else if(ss<=360){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(60+(ss-330)*1))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(ss-330)*1))),mPaint); }
7.分针转动:
mPaint.setStrokeWidth(timeLineS_W);//设置分针宽度 r=circleX-bordWidth;//还原 r=r-timeLineB_H-50;//设置分针长度 //分针随秒针转动 if(mm<=5){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-mm*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(mm*6))), mPaint); }else if(mm<=10){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(mm-5)*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(30+(mm-5)*6))), mPaint); }else if(mm<=15){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(mm-10)*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(60+(mm-10)*6))), mPaint); }else if(mm<=20){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians((mm-15)*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-(mm-15)*6))), mPaint); }else if(mm<=25){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(30+(mm-20)*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(mm-20)*6))), mPaint); }else if(mm<=30){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(60+(mm-25)*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(mm-25)*6))), mPaint); }else if(mm<=35){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-(mm-30)*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians((mm-30)*6))), mPaint); }else if(mm<=40){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(mm-35)*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(30+(mm-35)*6))), mPaint); }else if(mm<=45){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-60-(mm-40)*6))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(60+(mm-40)*6))), mPaint); }else if(mm<=50){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians((mm-45)*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-(mm-45)*6))),mPaint); }else if(mm<=55){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(30+(mm-50)*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(mm-50)*6))),mPaint); }else if(mm<=60){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(60+(mm-55)*6))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-30-(mm-55)*6))),mPaint); }
8.时针转动:
r=circleX-bordWidth;//还原 r=r-timeLineB_H-120;//设置时针长度 //时针随分针转动 if(hh<=3){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-hh*30))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(hh*30))), mPaint); }else if(hh<=6){ canvas.drawLine(circleX,circleX,(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians((hh-3)*30))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians(90-(hh-3)*30))), mPaint); }else if(hh<=9){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-(hh-6)*30))),(float)(circleX+(r-timeLineS_H)*Math.cos(Math.toRadians((hh-6)*30))), mPaint); }else if(hh<=12){ canvas.drawLine(circleX,circleX,(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians((hh-9)*30))),(float)(circleX-(r-timeLineS_H)*Math.cos(Math.toRadians(90-(hh-9)*30))),mPaint); }
9.绘制刻度值:
//写时刻数字 mPaint.setTextSize(26); mPaint.setStrokeWidth(2); mPaint.setColor(bordColor); canvas.drawText(12, circleX-timeLineB_W, bordWidth+timeLineB_H+30, mPaint); canvas.drawText(1, (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60)))-20,(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30)))+20, mPaint); canvas.drawText(2, (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30)))-20,(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60)))+20, mPaint); canvas.drawText(3, getWidth()-bordWidth-timeLineB_H-20, circleX+10, mPaint); canvas.drawText(4,(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30)))-20,(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60))), mPaint); canvas.drawText(5, (float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60)))-15,(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30)))-5, mPaint); canvas.drawText(6,circleX-10, getWidth()-bordWidth-timeLineB_H-10, mPaint); canvas.drawText(7, (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60))),(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(30)))-10, mPaint); canvas.drawText(8, (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30)))+10,(float)(circleX+(r-timeLineB_H)*Math.cos(Math.toRadians(60)))+5, mPaint); canvas.drawText(9, bordWidth+timeLineB_H+10, circleX+10, mPaint); canvas.drawText(10, (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30)))+10,(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60)))+20, mPaint); canvas.drawText(11, (float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(60))),(float)(circleX-(r-timeLineB_H)*Math.cos(Math.toRadians(30)))+25, mPaint);
在做的过程中会发现规律,就是每个大刻度(30度)即12点-1点,1点-2点......这之间所用的代码是相同的,只需要改变一下转动的角度值即可,