前言
自定义View实现时钟涉及到三角函数,如果你对三角函数不甚了解或者已经遗忘,请参考我的博客:圆与三角函数的公式与使用 这篇博客详细解释了三角函数公式与对应坐标关系。里面也举例了时钟的实现,只不过是用Jetpack compose的自定义View实现的时钟。而这篇博客用老的方式继承View实现。
效果图
代码
/** * 时钟View */ class ClockView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { private var mCalendar = Calendar.getInstance() private var mWidth = 0 private var mHeight = 0 private var mRadius = 0 private val mCentralPoint = Point() private val mHourPaint = Paint() private val mMinutePaint = Paint() private val mSecondsPaint = Paint() private val mScalePaint = Paint() private val mTextPaint = Paint() init { //时 mHourPaint.color = Color.WHITE mHourPaint.flags = Paint.ANTI_ALIAS_FLAG mHourPaint.strokeWidth = 4f mHourPaint.strokeCap = Paint.Cap.ROUND //分 mMinutePaint.color = Color.WHITE mMinutePaint.flags = Paint.ANTI_ALIAS_FLAG mMinutePaint.strokeWidth = 2f mMinutePaint.strokeCap = Paint.Cap.ROUND //秒 mSecondsPaint.color = Color.RED mSecondsPaint.flags = Paint.ANTI_ALIAS_FLAG mSecondsPaint.strokeCap = Paint.Cap.ROUND //刻度 mScalePaint.color = Color.WHITE mScalePaint.flags = Paint.ANTI_ALIAS_FLAG //文字 mTextPaint.color = Color.WHITE mTextPaint.flags = Paint.ANTI_ALIAS_FLAG mTextPaint.textAlign = Paint.Align.CENTER mTextPaint.textSize = 25f } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { mWidth = MeasureSpec.getSize(widthMeasureSpec) mHeight = MeasureSpec.getSize(heightMeasureSpec) mCentralPoint.x = mWidth / 2 mCentralPoint.y = mHeight / 2 mRadius = if (mWidth > mHeight) mHeight / 2 else mWidth / 2 super.onMeasure(widthMeasureSpec, heightMeasureSpec) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) mCalendar = Calendar.getInstance() drawClockBackground(canvas) drawCurrentHour(canvas) drawCurrentMinute(canvas) drawCurrentSecond(canvas) postInvalidateDelayed(100) } /** * 绘制时钟背景,画文字与刻度 */ private fun drawClockBackground(canvas: Canvas) { //画表盘 for (i in 1..60) { val angle = 360 / 60 * i + 48 val o = angle * Math.PI / 180 var scaleRadius = mRadius * 0.95 //刻度长度 if (i % 5 == 2) { scaleRadius = mRadius * 0.90 } val px1 = mCentralPoint.x + scaleRadius * Math.cos(o) val py1 = mCentralPoint.y - scaleRadius * Math.sin(o) val px2 = mCentralPoint.x + mRadius * Math.cos(o) val py2 = mCentralPoint.y - mRadius * Math.sin(o) canvas.drawLine(px1.toFloat(), py1.toFloat(), px2.toFloat(), py2.toFloat(), mScalePaint) } //画数字 for (i in 1..12) { val textAngle = 360 / 12 * i + 90 var textRadius = mRadius * 0.8 //数字的位置半径 val textO = textAngle * Math.PI / 180 val textPx = mCentralPoint.x + textRadius * Math.cos(textO) //因为文字有绘制基线导致12会比6显示更高一点,这样与时钟圆盘显示起来不在同一的圆心点上,这里减少圆心Y轴坐标做到 val textPy = (mCentralPoint.y + 10) - textRadius * Math.sin(textO) val text = if (12 - i == 0) 12 else 12 - i canvas.drawText(text.toString(), textPx.toFloat(), textPy.toFloat(), mTextPaint) } } /** * 绘制小时指针 */ private fun drawCurrentHour(canvas: Canvas) { val hour = mCalendar.get(Calendar.HOUR) val minute = mCalendar.get(Calendar.MINUTE) //得出分钟角度,转成负数是希望以顺时针旋转 var angle = -(360 / 12 * hour - 90) //补上当前分钟的角度到时钟上 >>> 当前分角度 除 12 angle -= 360 / 60 * minute / 60 val o = angle * Math.PI / 180 val radius = mRadius * 0.6 val px = mCentralPoint.x + radius * Math.cos(o) val py = mCentralPoint.y - radius * Math.sin(o) canvas.drawLine( mCentralPoint.x.toFloat(), mCentralPoint.y.toFloat(), px.toFloat(), py.toFloat(), mHourPaint ) } /** * 绘制分钟指针 */ private fun drawCurrentMinute(canvas: Canvas) { val minute = mCalendar.get(Calendar.MINUTE) val second = mCalendar.get(Calendar.SECOND) //得出分钟角度,转成负数是希望以顺时针旋转 var angle = -(360 / 60 * minute - 90) //补上当前秒的角度到分钟上 >>> 当前秒角度 除 60分钟 angle -= 360 / 60 * second / 60 val o = angle * Math.PI / 180 val radius = mRadius * 0.7 val px = mCentralPoint.x + radius * Math.cos(o) val py = mCentralPoint.y - radius * Math.sin(o) canvas.drawLine( mCentralPoint.x.toFloat(), mCentralPoint.y.toFloat(), px.toFloat(), py.toFloat(), mMinutePaint ) } /** * 绘制秒指针 */ private fun drawCurrentSecond(canvas: Canvas) { val second = mCalendar.get(Calendar.SECOND) //得出秒角度,转成负数是希望以顺时针旋转 val angle = -(360 / 60 * second - 90) val o = angle * Math.PI / 180 val radius = mRadius * 0.75 val px = mCentralPoint.x + radius * Math.cos(o) val py = mCentralPoint.y - radius * Math.sin(o) canvas.drawLine( mCentralPoint.x.toFloat(), mCentralPoint.y.toFloat(), px.toFloat(), py.toFloat(), mSecondsPaint ) } }
End
标签:canvas,自定义,val,mCentralPoint,toFloat,private,Android,Math,View From: https://www.cnblogs.com/guanxinjing/p/17073817.html