Android自定义控件之ClockView时钟效果

最近在网上看到一个仿锤子时钟效果的源代码,在此对其代码进行阅读,修改及分析,加强自己在自定义视图方面的技术及经验,便写该文章为以后参考阅读。代码实现效果如下:


Android自定义控件之ClockView时钟效果_第1张图片


   该自定义时钟由ClockView实现,其代码如下:
       
     
     
     
     
  1. import android.annotation.TargetApi;
  2. import android.content.Context;
  3. import android.graphics.Canvas;
  4. import android.graphics.Paint;
  5. import android.graphics.RectF;
  6. import android.os.Build;
  7. import android.support.annotation.ColorInt;
  8. import android.util.AttributeSet;
  9. import android.util.TypedValue;
  10. import android.view.View;
  11. import android.view.animation.AccelerateDecelerateInterpolator;
  12. import java.util.Calendar;
  13. /**
  14. * Created by Administrator on 2016/9/22.
  15. * 时钟控件
  16. */
  17. public class ClockView extends View {
  18. /**时钟背景颜色*/
  19. @ColorInt
  20. protected static final int CLOCK_BACKGROUND_COLOR= 0xFFF0F0F0;
  21. /**时钟圆环颜色*/
  22. @ColorInt
  23. protected static final int CLOCK_RING_COLOR=0xFFc9c9c9;
  24. /**字体颜色*/
  25. @ColorInt
  26. protected static final int TEXT_COLOR = 0xFF141414;
  27. /**时钟和分钟的颜色*/
  28. protected static final int HOUR_MINUTE_COLOR = 0xFF5B5B5B;
  29. /**秒钟的颜色*/
  30. @ColorInt
  31. private static final int SECOND_COLOR = 0xFFB55050;
  32. @ColorInt
  33. private static final int CLOCK_SCALE_COLOR=0xffc9c9c9;
  34. /**时钟最小尺寸*/
  35. private static final int CLOCK_MIN_SIZE=200;
  36. /**时钟及分钟的宽度*/
  37. private static final int HOUR_MINUTE_WIDTH = 16;
  38. /**秒钟的宽度*/
  39. private static final int SECOND_WIDTH = 8;
  40. /**时钟刻度的宽度*/
  41. private static final int SCALE_WIDTH=4;
  42. //每秒 秒针移动6°
  43. private static final int DEGREE = 6;
  44. /**时钟文本*/
  45. private static final String[] CLOCK_TEXT={"12","1","2","3","4","5","6","7","8","9","10","11"};
  46. /**时*/
  47. private float hour=5;
  48. /**分*/
  49. private float minute=30;
  50. /**秒*/
  51. private float second=5;
  52. /**绘制时钟的Paint*/
  53. private Paint hourPaint;
  54. /**绘制分钟的Paint*/
  55. private Paint minutePaint;
  56. /**绘制秒钟的Paint*/
  57. private Paint secondPaint;
  58. /**圆环的宽度*/
  59. private int clockRingWidth=10;
  60. /**时钟大小*/
  61. private int clockSize;
  62. /**绘制时钟的Paint*/
  63. private Paint clockPaint;
  64. /**绘制时钟圆环的Paint*/
  65. private Paint clockRingPaint;
  66. /**时钟中心外部圆*/
  67. private Paint clockCenterOuterCirclePaint;
  68. /**时钟中心内部圆*/
  69. private Paint clockCenterInnerCirclePaint;
  70. /**绘制时钟刻度的Paint*/
  71. private Paint clockScalePaint;
  72. /**绘制时钟文本的Paint*/
  73. private Paint clockTextPaint;
  74. /**获取时间的日历工具*/
  75. private Calendar calendar=null;
  76. public ClockView(Context context) {
  77. super(context);
  78. initView();
  79. }
  80. public ClockView(Context context, AttributeSet attrs) {
  81. super(context, attrs);
  82. initView();
  83. }
  84. public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
  85. super(context, attrs, defStyleAttr);
  86. initView();
  87. }
  88. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  89. public ClockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  90. super(context, attrs, defStyleAttr, defStyleRes);
  91. initView();
  92. }
  93. protected void initView(){
  94. clockPaint=new Paint();
  95. clockPaint.setColor(CLOCK_BACKGROUND_COLOR);
  96. clockPaint.setAntiAlias(true);
  97. clockRingPaint=new Paint();
  98. clockRingPaint.setColor(CLOCK_RING_COLOR);
  99. clockRingPaint.setStrokeWidth(dp2px(clockRingWidth));
  100. clockRingPaint.setStyle(Paint.Style.STROKE);
  101. clockRingPaint.setAntiAlias(true);
  102. //添加阴影 0x80000000
  103. clockRingPaint.setShadowLayer(4, 2, 2, 0x80000000);
  104. hourPaint = new Paint();
  105. hourPaint.setAntiAlias(true);
  106. hourPaint.setColor(HOUR_MINUTE_COLOR);
  107. hourPaint.setStrokeWidth(HOUR_MINUTE_WIDTH);
  108. //设置为圆角
  109. hourPaint.setStrokeCap(Paint.Cap.ROUND);
  110. //添加阴影
  111. hourPaint.setShadowLayer(4, 0, 0, 0x80000000);
  112. minutePaint = new Paint();
  113. minutePaint.setAntiAlias(true);
  114. minutePaint.setColor(HOUR_MINUTE_COLOR);
  115. minutePaint.setStrokeWidth(HOUR_MINUTE_WIDTH);
  116. //设置为圆角
  117. minutePaint.setStrokeCap(Paint.Cap.ROUND);
  118. //添加阴影
  119. minutePaint.setShadowLayer(4, 0, 0, 0x80000000);
  120. secondPaint = new Paint();
  121. secondPaint.setAntiAlias(true);
  122. secondPaint.setColor(SECOND_COLOR);
  123. secondPaint.setStrokeWidth(SECOND_WIDTH);
  124. //设置为圆角
  125. secondPaint.setStrokeCap(Paint.Cap.ROUND);
  126. //添加阴影
  127. secondPaint.setShadowLayer(4, 3, 0, 0x80000000);
  128. clockCenterOuterCirclePaint = new Paint();
  129. clockCenterOuterCirclePaint.setAntiAlias(true);
  130. clockCenterOuterCirclePaint.setColor(HOUR_MINUTE_COLOR);
  131. //添加阴影
  132. clockCenterOuterCirclePaint.setShadowLayer(5, 0, 0, 0x80000000);
  133. clockCenterInnerCirclePaint = new Paint();
  134. clockCenterInnerCirclePaint.setAntiAlias(true);
  135. clockCenterInnerCirclePaint.setColor(SECOND_COLOR);
  136. //添加阴影
  137. clockCenterInnerCirclePaint.setShadowLayer(5, 0, 0, 0x80000000);
  138. clockScalePaint=new Paint();
  139. clockScalePaint.setAntiAlias(true);
  140. clockScalePaint.setColor(CLOCK_SCALE_COLOR);
  141. //设置为圆角
  142. clockScalePaint.setStrokeCap(Paint.Cap.ROUND);
  143. clockScalePaint.setStrokeWidth(SCALE_WIDTH);
  144. clockTextPaint = new Paint();
  145. clockTextPaint.setAntiAlias(true);
  146. clockTextPaint.setStrokeWidth(1f);
  147. clockTextPaint.setColor(TEXT_COLOR);
  148. clockTextPaint.setTextSize(sp2px(13));
  149. }
  150. @Override
  151. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  152. //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  153. int width=MeasureSpec.getSize(widthMeasureSpec);
  154. clockSize=dp2px(CLOCK_MIN_SIZE);
  155. if(clockSize>width){
  156. width=clockSize;
  157. }else{
  158. clockSize = width;
  159. }
  160. setMeasuredDimension(width, width);
  161. }
  162. @Override
  163. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  164. super.onSizeChanged(w, h, oldw, oldh);
  165. if(w!=oldw||h!=oldh){
  166. clockSize=w;
  167. }
  168. int minSize=dp2px(CLOCK_MIN_SIZE);
  169. if(clockSize<minSize){
  170. clockSize=minSize;
  171. }
  172. }
  173. private void getTime(){
  174. calendar=Calendar.getInstance();
  175. hour = calendar.get(Calendar.HOUR);
  176. minute = calendar.get(Calendar.MINUTE);
  177. second = calendar.get(Calendar.SECOND);
  178. System.out.println(hour + ":" + minute + ":" + second);
  179. }
  180. @Override
  181. protected void onDraw(Canvas canvas) {
  182. super.onDraw(canvas);
  183. getTime();
  184. canvas.translate(clockSize / 2, clockSize / 2);
  185. drawClock(canvas);
  186. drawClockRing(canvas);
  187. drawClockScale(canvas);
  188. drawClockScaleText(canvas);
  189. drawHourPointer(canvas);
  190. drawMinutePointer(canvas);
  191. drawCenterOuterCircle(canvas);
  192. drawSecondPointer(canvas,second*DEGREE);
  193. drawCenterInnerCircle(canvas);
  194. postInvalidateDelayed(1000);
  195. }
  196. /**
  197. * 画表盘背景
  198. *
  199. * @param canvas 画布
  200. */
  201. private void drawClock(Canvas canvas) {
  202. canvas.drawCircle(0, 0, clockSize / 2 - 4, clockPaint);
  203. canvas.save();
  204. }
  205. /**
  206. * 画表盘最外层圆环
  207. *
  208. * @param canvas 画布
  209. */
  210. private void drawClockRing(Canvas canvas) {
  211. canvas.save();
  212. float radius =clockSize / 2 - dp2px(clockRingWidth + 6) / 2;
  213. RectF rectF = new RectF(-radius, -radius, radius, radius);
  214. clockRingPaint.setStrokeCap(Paint.Cap.ROUND);
  215. canvas.drawArc(rectF, 0, 360, false, clockRingPaint);
  216. canvas.restore();
  217. }
  218. /**
  219. * 画时针
  220. *
  221. * @param canvas 画布
  222. */
  223. private void drawHourPointer(Canvas canvas) {
  224. int length = clockSize / 4;
  225. canvas.save();
  226. //这里没有算秒钟对时钟的影响
  227. float degree = hour * 5 * DEGREE + minute / 2f;
  228. canvas.rotate(degree, 0, 0);
  229. canvas.drawLine(0, 0, 0, -length, hourPaint);
  230. canvas.restore();
  231. }
  232. /**
  233. * 画分针
  234. *
  235. * @param canvas 画布
  236. */
  237. private void drawMinutePointer(Canvas canvas) {
  238. int length = clockSize / 3-dp2px(2);
  239. canvas.save();
  240. float degree = minute * DEGREE + second / 10f;
  241. canvas.rotate(degree, 0, 0);
  242. canvas.drawLine(0, 0, 0, -length, minutePaint);
  243. canvas.restore();
  244. }
  245. /**
  246. * 画秒针
  247. *
  248. * @param canvas 画布
  249. */
  250. private void drawSecondPointer(Canvas canvas, float degrees) {
  251. int length = clockSize / 2-dp2px(15);
  252. canvas.save();
  253. canvas.rotate(degrees);
  254. canvas.drawLine(0, length / 5, 0, -length * 4 / 5, secondPaint);
  255. canvas.restore();
  256. }
  257. /**
  258. * 绘制时钟刻度
  259. * @param canvas
  260. */
  261. private void drawClockScale(Canvas canvas){
  262. canvas.save();
  263. int startY=clockSize / 2 - dp2px(clockRingWidth + 6) / 2-dp2px(clockRingWidth)/2;
  264. int endY=startY-dp2px(5);
  265. int endY2=startY-dp2px(10);
  266. //canvas.rotate(-180);
  267. for (int i=0; i<=360; i+=DEGREE){
  268. if(i%5==0) {
  269. canvas.drawLine(0, startY, 0, endY2, clockScalePaint);
  270. }else{
  271. canvas.drawLine(0, startY, 0, endY, clockScalePaint);
  272. }
  273. canvas.rotate(DEGREE);
  274. }
  275. canvas.restore();
  276. }
  277. /**
  278. * 绘制时钟刻度文本
  279. * @param canvas
  280. */
  281. private void drawClockScaleText(Canvas canvas){
  282. canvas.save();
  283. //canvas.rotate(-180f);
  284. float dis=clockTextPaint.measureText(CLOCK_TEXT[1])/2;
  285. Paint.FontMetrics fontMetrics=clockTextPaint.getFontMetrics();
  286. float fontHeight=fontMetrics.descent-fontMetrics.ascent;
  287. float radius=clockSize / 2 - dp2px(clockRingWidth + 6) / 2-dp2px(clockRingWidth)/2-dp2px(10)-fontHeight/2;
  288. for(int i=0;i<CLOCK_TEXT.length;i++){
  289. float x=(float) (Math.sin(Math.PI - Math.PI / 6 * i) * radius - dis);
  290. if(i==0){
  291. x -=dis;
  292. }
  293. float y= (float) (Math.cos(Math.PI-Math.PI/6*i)*radius+dis);
  294. canvas.drawText(CLOCK_TEXT[i],x,y,clockTextPaint);
  295. }
  296. canvas.restore();
  297. }
  298. /**
  299. * 画中心黑圆
  300. *
  301. * @param canvas 画布
  302. */
  303. private void drawCenterOuterCircle(Canvas canvas) {
  304. int radius = clockSize / 20;
  305. canvas.save();
  306. canvas.drawCircle(0, 0, radius, clockCenterOuterCirclePaint);
  307. canvas.restore();
  308. }
  309. /**
  310. * 红色中心圆
  311. *
  312. * @param canvas 画布
  313. */
  314. private void drawCenterInnerCircle(Canvas canvas) {
  315. int radius = clockSize / 40;
  316. canvas.save();
  317. canvas.drawCircle(0, 0, radius, clockCenterInnerCirclePaint);
  318. canvas.restore();
  319. }
  320. /**
  321. * 将 dp 转换为 px
  322. *
  323. * @param dp 需转换数
  324. * @return 返回转换结果
  325. */
  326. private int dp2px(int dp) {
  327. return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
  328. }
  329. private int sp2px(int sp) {
  330. return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
  331. }
  332. }
        在ClockView中,主要是时间界面的绘制,绘制需要用到android中的Paint及Canvas画布,并在onDraw方法中进行绘制。在onDraw方法的代码如下:
        

  1. @Override
  2. protectedvoid onDraw(Canvas canvas){
  3. super.onDraw(canvas);
  4. getTime();
  5. canvas.translate(clockSize/2, clockSize/2);
  6. drawClock(canvas);
  7. drawClockRing(canvas);
  8. drawClockScale(canvas);
  9. drawClockScaleText(canvas);
  10. drawHourPointer(canvas);
  11. drawMinutePointer(canvas);
  12. drawCenterOuterCircle(canvas);
  13. drawSecondPointer(canvas,second*DEGREE);
  14. drawCenterInnerCircle(canvas);
  15. postInvalidateDelayed(1000);
  16. }

      首先通过Calendar类获取时/分/秒,并通过Canvas的translate方法,将画布的坐标系移动到控件中心,然后依次绘制时钟的背景,背景圆环,刻度,时钟刻度文本,时钟指针,分钟指针,时钟内部圆环,秒钟指针及秒钟红色圆环,并通过postInvalidateDelayed方法,延迟1秒钟,重新绘制界面,以达到秒钟指针转动的效果。


你可能感兴趣的:(android开发)