观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17073817.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

  自定义View实现时钟涉及到三角函数,如果你对三角函数不甚了解或者已经遗忘,请参考我的博客:圆与三角函数的公式与使用   这篇博客详细解释了三角函数公式与对应坐标关系。里面也举例了时钟的实现,只不过是用Jetpack compose的自定义View实现的时钟。而这篇博客用老的方式继承View实现。

例子1

效果图

 

代码

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Point
import android.util.AttributeSet
import android.view.View
import java.util.*

/**
 * 时钟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)
        //求当前分钟的比例
        val proportion = 360f / 60f * minute / 360
        //将30度乘分钟比得出当前小时需要多移动的角度
        angle -= (360 / 12 * proportion).toInt()
        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)
        //求当前秒钟的比例
        val proportion = 360f / 60f * second / 360
        //将6度乘秒钟比得出当前分钟需要多移动的角度
        angle -= (360 / 60 * proportion).toInt()
        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
        )
    }
}

例子2

效果图

 

代码

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import java.util.*

/**
 * 时钟View
 */
class ClockView2(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 mOvalPaint = Paint()

    init {
        //
        mHourPaint.color = Color.WHITE
        mHourPaint.flags = Paint.ANTI_ALIAS_FLAG
        mHourPaint.strokeCap = Paint.Cap.ROUND
        //
        mMinutePaint.color = Color.WHITE
        mMinutePaint.flags = Paint.ANTI_ALIAS_FLAG
        mMinutePaint.strokeWidth = 2f
        mMinutePaint.strokeCap = Paint.Cap.ROUND
        //
        mSecondsPaint.flags = Paint.ANTI_ALIAS_FLAG
        mSecondsPaint.strokeCap = Paint.Cap.ROUND
        //刻度
        mScalePaint.color = Color.WHITE
        mScalePaint.flags = Paint.ANTI_ALIAS_FLAG
        mScalePaint.strokeWidth = 8f
        mScalePaint.strokeCap = Paint.Cap.ROUND
        //圆形
        mOvalPaint.color = Color.WHITE
        mOvalPaint.flags = Paint.ANTI_ALIAS_FLAG

    }

    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)
        drawOval(canvas)
        postInvalidateDelayed(300)
    }

    /**
     * 绘制时钟背景,画文字与刻度
     */
    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 //刻度长度
            mScalePaint.color = Color.WHITE
            if (i % 5 == 2) {
                scaleRadius = mRadius * 0.75
            }
            val px1 = mCentralPoint.x + scaleRadius * Math.cos(o)
            val py1 = mCentralPoint.y - scaleRadius * Math.sin(o)

            val px2 = mCentralPoint.x + (mRadius * 0.95) * Math.cos(o)
            val py2 = mCentralPoint.y - (mRadius * 0.95) * Math.sin(o)
            canvas.drawLine(px1.toFloat(), py1.toFloat(), px2.toFloat(), py2.toFloat(), mScalePaint)
        }
    }

    /**
     * 绘制小时指针
     */
    private fun drawCurrentHour(canvas: Canvas) {
        val hour = mCalendar.get(Calendar.HOUR)
        val minute = mCalendar.get(Calendar.MINUTE)
        //得出分钟角度,转成负数是希望以顺时针旋转
        var angle = -(360 / 12 * hour - 90)
        //求当前分钟的比例
        val proportion = 360f / 60f * minute / 360
        //将30度乘分钟比得出当前小时需要多移动的角度
        angle -= (360 / 12 * proportion).toInt()
        val o = angle * Math.PI / 180
        val px1 = mCentralPoint.x + (mRadius * 0.15) * Math.cos(o)
        val py1 = mCentralPoint.y - (mRadius * 0.15) * Math.sin(o)
        val px2 = mCentralPoint.x + (mRadius * 0.6) * Math.cos(o)
        val py2 = mCentralPoint.y - (mRadius * 0.6) * Math.sin(o)
        mHourPaint.color = Color.WHITE
        mHourPaint.strokeWidth = 18f
        canvas.drawLine(
            px1.toFloat(),
            py1.toFloat(),
            px2.toFloat(),
            py2.toFloat(),
            mHourPaint
        )
        mHourPaint.strokeWidth = 7f
        val px3 = mCentralPoint.x + (mRadius * 0.6) * Math.cos(o)
        val py3 = mCentralPoint.y - (mRadius * 0.6) * Math.sin(o)
        canvas.drawLine(
            mCentralPoint.x.toFloat(),
            mCentralPoint.y.toFloat(),
            px3.toFloat(),
            py3.toFloat(),
            mHourPaint
        )
        //画整点标记
        if (minute <= 1) {
            mHourPaint.strokeWidth = 9f
            mHourPaint.color = Color.parseColor("#3AC0E5")
            val px4 = mCentralPoint.x + (mRadius * 0.75) * Math.cos(o)
            val py4 = mCentralPoint.y - (mRadius * 0.75) * Math.sin(o)
            val px5 = mCentralPoint.x + (mRadius * 0.95) * Math.cos(o)
            val py5 = mCentralPoint.y - (mRadius * 0.95) * Math.sin(o)
            canvas.drawLine(
                px4.toFloat(),
                py4.toFloat(),
                px5.toFloat(),
                py5.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)
        //求当前秒钟的比例
        val proportion = 360f / 60f * second / 360
        //将6度乘秒钟比得出当前分钟需要多移动的角度
        angle -= (360 / 60 * proportion).toInt()
        val o = angle * Math.PI / 180
        val px1 = mCentralPoint.x + (mRadius * 0.15) * Math.cos(o)
        val py1 = mCentralPoint.y - (mRadius * 0.15) * Math.sin(o)
        val px2 = mCentralPoint.x + (mRadius * 0.75) * Math.cos(o)
        val py2 = mCentralPoint.y - (mRadius * 0.75) * Math.sin(o)
        mMinutePaint.strokeWidth = 18f
        canvas.drawLine(
            px1.toFloat(),
            py1.toFloat(),
            px2.toFloat(),
            py2.toFloat(),
            mMinutePaint
        )
        mMinutePaint.strokeWidth = 7f
        val px3 = mCentralPoint.x + (mRadius * 0.6) * Math.cos(o)
        val py3 = mCentralPoint.y - (mRadius * 0.6) * Math.sin(o)
        canvas.drawLine(
            mCentralPoint.x.toFloat(),
            mCentralPoint.y.toFloat(),
            px3.toFloat(),
            py3.toFloat(),
            mMinutePaint
        )
    }

    /**
     * 绘制秒指针
     */
    private fun drawCurrentSecond(canvas: Canvas) {
        //设置渐变色
        val linearGradient = RadialGradient(
            mCentralPoint.x.toFloat(), mCentralPoint.y.toFloat(), mRadius.toFloat(),
            Color.parseColor("#1DBBFF"),
            Color.parseColor("#48F3D0"),
            Shader.TileMode.CLAMP
        )
        mSecondsPaint.setShader(linearGradient)
        val second = mCalendar.get(Calendar.SECOND)
        //靠近圆心的2个坐标,这里增加了30秒是为了得到对立面的角度
        val angle1 = -(360 / 60 * (second + 30) - 90)
        //这里增加正负3个角度的偏差,让秒钟起点的2个坐标有一定的距离,形成三角形的2个点的坐标
        val o1 = (angle1 + 3f) * Math.PI / 180
        val o2 = (angle1 - 3f) * Math.PI / 180
        val radius1 = mRadius * 0.2
        val px1 = (mCentralPoint.x) + radius1 * Math.cos(o1)
        val py1 = (mCentralPoint.y) - radius1 * Math.sin(o1)
        val px2 = (mCentralPoint.x) + radius1 * Math.cos(o2)
        val py2 = (mCentralPoint.y) - radius1 * Math.sin(o2)
        //圆周坐标
        //得出秒角度,转成负数是希望以顺时针旋转
        val angle = -(360 / 60 * second - 90)
        val o = angle * Math.PI / 180
        val radius = mRadius * 0.95
        val px3 = mCentralPoint.x + radius * Math.cos(o)
        val py3 = mCentralPoint.y - radius * Math.sin(o)
        //画一个非常长的三角形
        val path = Path()
        path.moveTo(px1.toFloat(), py1.toFloat())
        path.lineTo(px2.toFloat(), py2.toFloat())
        path.lineTo(px3.toFloat(), py3.toFloat())
        path.close()
        canvas.drawPath(path, mSecondsPaint)
    }

    private fun drawOval(canvas: Canvas) {
        mOvalPaint.color = Color.WHITE
        canvas.drawOval(
            mCentralPoint.x.toFloat() - 12f,
            mCentralPoint.y.toFloat() - 12f,
            mCentralPoint.x.toFloat() + 12f,
            mCentralPoint.y.toFloat() + 12f,
            mOvalPaint
        )
        mOvalPaint.color = Color.parseColor("#3AC0E5")
        canvas.drawOval(
            mCentralPoint.x.toFloat() - 8f,
            mCentralPoint.y.toFloat() - 8f,
            mCentralPoint.x.toFloat() + 8f,
            mCentralPoint.y.toFloat() + 8f,
            mOvalPaint
        )
    }
}

 

 

 

 

 

 

End

posted on 2023-01-29 20:57  观心静  阅读(436)  评论(0编辑  收藏  举报