我们今天来自定义一个闹钟。效果如下:

具体代码请参考:Demo

第一步:自定义属性

在文件app/src/main/res/values/attrs.xml中加入自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="ClockView"><attr name="circle_color" format="color"/><attr name="circle_scale_color" format="color"/><attr name="sec_point_color" format="color"/><attr name="min_point_color" format="color"/><attr name="hour_point_color" format="color"/><attr name="dot_color" format="color"/></declare-styleable>...
</resources>

第二步:重写onMeasure 、onLayout、onDraw方法

重写onMeasure主要是为了解决,当wrap_content时,视图会占满整个父布局,所以要设置一个默认值。

重写onLayout是因为遵守良好的习惯。因为onMeasure可能会重复调用多次,那么视图的宽高就可能在最后一次才能确定下来。当然这种情况更可能出现在继承ViewGroup中的自定义视图中。我们本例是继承View,所以一般不会出现上述情况。onLayout中一定能拿到视图的宽高而且是确定下来的。
onDraw方法一般都需要重写。

package com.wong.clock;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;import java.util.Calendar;public class ClockView extends View {/*画笔*/private Paint mPaintCircle = new Paint();private Paint mPaintDot = new Paint();private Paint mPaintScale = new Paint();private Paint mPaintHour = new Paint();private Paint mPaintMin = new Paint();private Paint mPaintSec = new Paint();private int width = 0;private int height = 0;private int circleColor = Color.WHITE;private int scaleColor = Color.GRAY;private int dotColor = Color.BLACK;private int hourPointColor = Color.BLACK;private int minPointColor = Color.BLACK;private int secPointColor = Color.RED;private float hourPointWidth = 12f;private float minPointWidth = 8f;private float secPointWidth = 2f;private float dotWidth = 0;public void setCircleColor(int circleColor){this.circleColor = circleColor;}public void setDotColor(int dotColor) {this.dotColor = dotColor;}public void setHourPointColor(int hourPointColor) {this.hourPointColor = hourPointColor;}public void setMinPointColor(int minPointColor) {this.minPointColor = minPointColor;}public void setSecPointColor(int secPointColor) {this.secPointColor = secPointColor;}public void setHourPointWidth(float hourPointWidth) {this.hourPointWidth = hourPointWidth;}public void setMinPointWidth(float minPointWidth) {this.minPointWidth = minPointWidth;}public void setSecPointWidth(float secPointWidth) {this.secPointWidth = secPointWidth;}public ClockView(Context context) {super(context);init(context,null);}public ClockView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(context,attrs);}public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context,attrs);}public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init(context,attrs);}private void init(@NonNull Context context,AttributeSet attrs){/*初始化xml属性的值*/if(attrs != null) {TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ClockView);circleColor = typedArray.getColor(R.styleable.ClockView_circle_color,circleColor);scaleColor = typedArray.getColor(R.styleable.ClockView_circle_scale_color,scaleColor);dotColor = typedArray.getColor(R.styleable.ClockView_dot_color,dotColor);hourPointColor = typedArray.getColor(R.styleable.ClockView_hour_point_color,hourPointColor);minPointColor = typedArray.getColor(R.styleable.ClockView_min_point_color,minPointColor);secPointColor = typedArray.getColor(R.styleable.ClockView_sec_point_color,secPointColor);hourPointWidth = typedArray.getDimension(R.styleable.ClockView_hour_point_width,hourPointWidth);minPointWidth = typedArray.getDimension(R.styleable.ClockView_min_point_width,minPointWidth);secPointWidth = typedArray.getDimension(R.styleable.ClockView_sec_point_width,secPointWidth);dotWidth = typedArray.getDimension(R.styleable.ClockView_dot_width,dotWidth);typedArray.recycle();}/*表盘*/mPaintCircle.setStyle(Paint.Style.FILL);mPaintCircle.setAntiAlias(true);mPaintCircle.setColor(circleColor);/*刻度*/mPaintScale.setStyle(Paint.Style.FILL);mPaintScale.setAntiAlias(true);mPaintScale.setColor(scaleColor);/*圆心的圆*/mPaintDot.setStyle(Paint.Style.FILL);mPaintDot.setAntiAlias(true);mPaintDot.setColor(dotColor);/*hour*/mPaintHour.setAntiAlias(true);mPaintHour.setStrokeWidth(hourPointWidth);mPaintHour.setStyle(Paint.Style.FILL);mPaintHour.setColor(hourPointColor);/*min*/mPaintMin.setAntiAlias(true);mPaintMin.setStrokeWidth(minPointWidth);mPaintMin.setStyle(Paint.Style.FILL);mPaintMin.setColor(minPointColor);/*sec*/mPaintSec.setAntiAlias(true);mPaintSec.setStrokeWidth(secPointWidth);mPaintSec.setStyle(Paint.Style.FILL);mPaintSec.setColor(secPointColor);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int widthW = widthSize - getPaddingLeft()-getPaddingRight();int heightH = heightSize - getPaddingTop() - getPaddingBottom();switch (widthMode){case MeasureSpec.EXACTLY:case MeasureSpec.AT_MOST:if(getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) {width = widthW;}else if(getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT){width  = (int)getResources().getDimension(R.dimen.clock_view_width);}else{width = widthW;}break;case MeasureSpec.UNSPECIFIED:width = widthW;break;}switch (heightMode){case MeasureSpec.EXACTLY:case MeasureSpec.AT_MOST:if(getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) {height = heightH;}else if(getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT){height = (int)getResources().getDimension(R.dimen.clock_view_height);}else{height = heightH;}break;case MeasureSpec.UNSPECIFIED:height = heightH;break;}setMeasuredDimension(width,height);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);width = getMeasuredWidth();height = getMeasuredHeight();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);/*设置画布的颜色*/canvas.drawColor(Color.WHITE);int radius = Math.min(width,height)/2;/*画刻盘*/canvas.drawCircle(width/2,height/2,radius,mPaintCircle);/*绘制12个点,代表12小时*/float scaleRadius = radius / 24;float dotY = (float) height/2-radius+5*scaleRadius/4;for(int i = 0; i < 12; i++){canvas.drawCircle(width/2, dotY, scaleRadius, mPaintScale);canvas.rotate(30f,width/2,height/2);}/*绘制时针、分针、秒针*/Calendar calendar = Calendar.getInstance();float hour = calendar.get(Calendar.HOUR);float min = calendar.get(Calendar.MINUTE);float sec = calendar.get(Calendar.SECOND);/*时针转过的角度*/float angleHour = hour * (float) (360 / 12) + min/60*30;/*分针转过的角度*/float angleMin = min * 6;/*秒针转过的角度*/float angleSec = sec * 6;/*绘制时针*/float hourY = (float) height/2-radius+(float) radius / 2;canvas.save();canvas.rotate(angleHour,width/2,height/2);canvas.drawLine(width/2,height/2,width/2,hourY,mPaintHour);canvas.restore();/*绘制分针*/float minY = (float) height/2-radius+(float) radius / 3;canvas.save();canvas.rotate(angleMin,width/2,height/2);canvas.drawLine(width/2,height/2,width/2,minY,mPaintMin);canvas.restore();/*绘制分针*/float secY = (float) height/2-radius+(float) radius / 5;canvas.save();canvas.rotate(angleSec,width/2,height/2);canvas.drawLine(width/2,height/2,width/2,secY,mPaintSec);canvas.restore();/*在圆心画个圆盖住三条指针的连接处*/float dotRadius = Math.max(dotWidth,(float) 0.05*radius);canvas.drawCircle(width/2,height/2,dotRadius,mPaintDot);/*每隔1秒就刷新*/postDelayed(new Runnable() {@Overridepublic void run() {invalidate();}},1000);}
}

代码的具体含义,请看代码中的注释。

第三步:在布局文件中使用闹钟视图

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.wong.clock.ClockViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"android:background="@color/colorPrimaryDark"app:circle_color="@color/colorPrimaryDark"app:circle_scale_color="@android:color/white"app:dot_color="@android:color/white"app:hour_point_color="@android:color/white"app:min_point_color="@android:color/white"app:sec_point_color="@android:color/holo_red_dark"app:sec_point_width="3dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

效果如前所示。

谢谢阅读!

自定义View——闹钟相关推荐

  1. Android自定义View 闹钟唤起播放闹钟铃声实现

    先上图看一下闹钟唤期页面的效果 实现的功能: 1:转动的图片根据天气情况更换 2:转动时间可以设置,转动结束,闹铃声音就结束 3:光圈颜色渐变效果 直接上代码啦: package com.exampl ...

  2. Android5.0自定义闹钟,Android自定义View 实现闹钟唤起播放闹钟铃声功能

    先上图看一下闹钟唤期页面的效果 实现的功能: 1:转动的图片根据天气情况更换 2:转动时间可以设置,转动结束,闹铃声音就结束 3:光圈颜色渐变效果 直接上代码啦: package com.yuekon ...

  3. Android自定义View绘制闹钟

    Android自定义View绘制闹钟 本文简单实现了一个闹钟,扩展View,Canvas绘制 效果如下: 代码如下: package com.gaofeng.mobile.clock_demo;imp ...

  4. 自定义view初学习(仿小米闹钟)

    我是第一次打自定义view相关代码,说错了大家请多多包涵 首先前导知识是自定义view创建时候要重写的方法onMeasure确定相当于父容器当前自定义view的位置 onDraw  用Canvas.曲 ...

  5. 自定义View之指南针(反编译别人的代码实现)

    一.说明 偶尔点开魅族手机内置的工具箱应用,发现其指南针做的还不错,就想模拟做一个类似的效果,在这里我们不准备自己从头开始编写代码,而是采用一点黑科技,首先,我们从魅族系统中导出工具箱应用的apk,然 ...

  6. 以小米时钟为demo学习自定义view过程总结

    首选感谢这位博主的分享,让我等新生有很好的范例学习 http://blog.csdn.net/qq_31715429/article/details/54668668点击打开链接 点击打开链接 点击打 ...

  7. Android自定义View基本步骤

    一.自定义属性 1.在res下的values下面新建attrs.xml 2.在布局中使用,声明命名空间 3.在自定义View构造方法中通过TypedArray获取属性 4.必须回收 array.rec ...

  8. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  9. Android 自定义View Canvas —— Bitmap

    Bitmap 绘制图片 常用的方法有一下几种 (1) drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint ...

最新文章

  1. 2014目标!!!!
  2. Scala中集合类型与java中集合类型转换
  3. 对整个矩阵元素进行计算:最大数、最小数、排序
  4. linux文件系统挂载磁盘,linux – 无法挂载磁盘(VFS:找不到ext4文件系统)
  5. 剑指offer之56-60题解
  6. web压力测试之siege
  7. 基于静态类型分析的java程序函数调用图构建方法研究,JAVA的静态方法调用
  8. React系列——React Fiber 架构介绍资料汇总(翻译+中文资料)
  9. C#中,控制台模式可以使用定时器吗?
  10. response.sendRedirect()和request.getRequestDispatcher().forward(request,reponse)的区别
  11. log4j.xml按照日期生成_荐读 | 进项发票快速生成凭证!这个功能太方便了!
  12. 金融级IT架构-数字银行的云原生架构解析
  13. 计算机真有趣作文,这真有趣作文(共6篇)
  14. 程序员深爱的bilibili后台源码泄露,看哔哩哔哩官方回应才放心了
  15. 网络摄像机·监控摄像机用 镜头驱动芯片MS41909 功能对标BU24036MW
  16. HALEY KOEHN--a good graphic designer recent years
  17. WIN2012远程桌面授权过期
  18. DevEco IDE 华为全系列远程真机免费调测
  19. 五种企业家,一定不要建自己的网站
  20. vue js 语音播报 语音读文字 window.speechSynthesis new SpeechSynthesisUtterance (补充无声音 问题解决办法)

热门文章

  1. 推陈、致新——城市更新促进美丽城市建设
  2. 木马另类删除文件的方法
  3. 4-20mA电路转换
  4. 说说QQ与微信以及支付宝
  5. Word删除空白页的方法(删除分页符)
  6. IIS安装过程中可能遇到的问题及解决方法
  7. render是什么意思
  8. unity实现迷雾效果
  9. 构建下一代开源社区,撬动万亿级产业
  10. 如何挑选自媒体平台进行创作?这3个关键需要把握