当前位置:首页 > 谈天说地

Android自定义View实现时钟功能

34资源网2022-09-13270

最近在练习自定义view, 想起之前面试的时候笔试有道题是写出自定义一个时钟的关键代码. 今天就来实现一下. 步骤依然是先分析, 再上代码.

实现效果

view分析

时钟主要分为五个部分:

1、中心点: 圆心位置

2、圆盘: 以中心点为圆心,drawcircle画个圆

3、刻度:

paint有个aip, setpatheffect可以根据path画特效, 那么刻度就可以根据圆的path画一个矩形path的特效, 并且这个api只会画特效, 不会画出圆.

/**
* shape: 特效的path, 这里传一个矩形
* advance: 两个特效path之间的间距, 即两个矩形的left间距
* phase: 特效起始位置的偏移
* style: 原始path拐弯的时候特效path的转换方式,这里用rotate跟着旋转即可
*/
pathdashpatheffect(path shape, float advance, float phase,
                              style style)

刻度又分两种, 粗一点刻度: 3、6、9、12, 和细一点的刻度. 两种特效又可以用sumpatheffect合起来画

sumpatheffect(patheffect first, patheffect second) 

4、时分秒指针

时分秒指针都是一个圆角矩形, 先把他们的位置计算出来, 然后旋转圆心去绘制不同角度的指针.

5、动画效果

timertask每隔一秒计算时间, 根据时间去换算当前时分秒指针的角度, 动态变量只有三个角度.

实现源码

//
// created by skylar on 2022/4/19.
//
class clockview : view {
    private var mtimer: timer? = null
    private val mcirclepaint: paint = paint()
    private val mpointerpaint: paint = paint()
    private val mtextpaint: paint = paint()

    private val mcirclepath: path = path()
    private val mhourpath: path = path()
    private val mminutepath: path = path()
    private val msecondpath: path = path()

    private lateinit var mpathmeasure: pathmeasure
    private lateinit var msumpatheffect: sumpatheffect

    private var mviewwidth = 0
    private var mviewheight = 0
    private var mcirclewidth = 6f
    private var mradius = 0f
    private var mrectradius = 20f
    private var mhoursdegree = 0f
    private var mminutesdegree = 0f
    private var msecondsdegree = 0f
    private var mcurrenttimeinsecond = 0l

    constructor(context: context) : super(context)

    constructor(context: context, attrs: attributeset?) : this(context, attrs, 0)

    constructor(
        context: context, attrs: attributeset?,
        defstyleattr: int
    ) : super(context, attrs, defstyleattr)

    init {
        mcirclepaint.color = color.black
        mcirclepaint.isantialias = true
        mcirclepaint.style = paint.style.stroke
        mcirclepaint.strokewidth = mcirclewidth

        mpointerpaint.color = color.black
        mpointerpaint.isantialias = true
        mpointerpaint.style = paint.style.fill

        mtextpaint.color = color.black
        mtextpaint.isantialias = true
        mtextpaint.style = paint.style.fill
        mtextpaint.textsize = 40f
    }

    override fun onsizechanged(w: int, h: int, oldw: int, oldh: int) {
        super.onsizechanged(w, h, oldw, oldh)
        mviewwidth = measuredwidth - paddingleft - paddingright
        mviewheight = measuredheight - paddingtop - paddingbottom

        mradius = mviewwidth / 2 - mcirclewidth
        mcirclepath.addcircle(0f, 0f, mradius, path.direction.cw)

        mpathmeasure = pathmeasure(mcirclepath, false)
        val minutesshapepath = path()
        val quartershapepath = path()
        minutesshapepath.addrect(0f, 0f, mradius * 0.01f, mradius * 0.06f, path.direction.cw)
        quartershapepath.addrect(0f, 0f, mradius * 0.02f, mradius * 0.06f, path.direction.cw)
        val minutesdashpatheffect = pathdashpatheffect(
            minutesshapepath,
            mpathmeasure.length / 60,
            0f,
            pathdashpatheffect.style.rotate
        )
        val quarterdashpatheffect = pathdashpatheffect(
            quartershapepath,
            mpathmeasure.length / 12,
            0f,
            pathdashpatheffect.style.rotate
        )
        msumpatheffect = sumpatheffect(minutesdashpatheffect, quarterdashpatheffect)

        val hourpointerheight = mradius * 0.5f
        val hourpointerwidth = mradius * 0.07f
        val hourrect = rectf(
            -hourpointerwidth / 2,
            -hourpointerheight * 0.7f,
            hourpointerwidth / 2,
            hourpointerheight * 0.3f
        )
        mhourpath.addroundrect(hourrect, mrectradius, mrectradius, path.direction.cw)

        val minutepointerheight = mradius * 0.7f
        val minutepointerwidth = mradius * 0.05f
        val minuterect = rectf(
            -minutepointerwidth / 2,
            -minutepointerheight * 0.8f,
            minutepointerwidth / 2,
            minutepointerheight * 0.2f
        )
        mminutepath.addroundrect(minuterect, mrectradius, mrectradius, path.direction.cw)

        val secondpointerheight = mradius * 0.9f
        val secondpointerwidth = mradius * 0.03f
        val secondrect = rectf(
            -secondpointerwidth / 2,
            -secondpointerheight * 0.8f,
            secondpointerwidth / 2,
            secondpointerheight * 0.2f
        )
        msecondpath.addroundrect(secondrect, mrectradius, mrectradius, path.direction.cw)

        startanimator()
    }

    override fun ondraw(canvas: canvas?) {
        super.ondraw(canvas)
        if (canvas == null) {
            return
        }
        canvas.translate((mviewwidth / 2).tofloat(), (mviewheight / 2).tofloat())

        //画圆盘
        mcirclepaint.patheffect = null
        canvas.drawpath(mcirclepath, mcirclepaint)

        //画刻度
        mcirclepaint.patheffect = msumpatheffect
        canvas.drawpath(mcirclepath, mcirclepaint)

        //时分秒指针
        mpointerpaint.color = color.black

        canvas.save()
        canvas.rotate(mhoursdegree)
        canvas.drawpath(mhourpath, mpointerpaint)
        canvas.restore()

        canvas.save()
        canvas.rotate(mminutesdegree)
        canvas.drawpath(mminutepath, mpointerpaint)
        canvas.restore()

        canvas.save()
        canvas.rotate(msecondsdegree)
        canvas.drawpath(msecondpath, mpointerpaint)
        canvas.restore()

        //画中心点
        mpointerpaint.color = color.white
        canvas.drawcircle(0f, 0f, mradius * 0.02f, mpointerpaint)
    }


    private fun startanimator() {
        val cal = calendar.getinstance()
        val hour = cal.get(calendar.hour)  //小时
        val minute = cal.get(calendar.minute)  //分
        val second = cal.get(calendar.second)  //秒
        mcurrenttimeinsecond = (hour * 60 * 60 + minute * 60 + second).tolong()

        if (mtimer == null) {
            mtimer = timer()
        } else {
            mtimer?.cancel()
            mtimertask.cancel()
        }
        mtimer?.schedule(mtimertask, 0, 1000)
    }

    private var mtimertask: timertask = object : timertask() {
        override fun run() {
            mcurrenttimeinsecond++
            computedegree()
            invalidate()
        }
    }

    //12小时 00:00:00 ~ 12:00:00
    private fun computedegree() {
        val secondsinoneroll = 12 * 60 * 60
        val currentseconds = mcurrenttimeinsecond % secondsinoneroll

        var leftseconds = currentseconds
        val hours = currentseconds / 60 / 60
        leftseconds = currentseconds - hours * 60 * 60
        val minutes = leftseconds / 60
        leftseconds -= minutes * 60
        val seconds = leftseconds % 60

        mhoursdegree = hours * 30f
        mminutesdegree = minutes * 6f
        msecondsdegree = seconds * 6f

    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持萬仟网。

看完文章,还可以扫描下面的二维码下载快手极速版领4元红包

快手极速版二维码

快手极速版新人见面礼

除了扫码领红包之外,大家还可以在快手极速版做签到,看视频,做任务,参与抽奖,邀请好友赚钱)。

邀请两个好友奖最高196元,如下图所示:

快手极速版邀请好友奖励

扫描二维码推送至手机访问。

版权声明:本文由34楼发布,如需转载请注明出处。

本文链接:https://www.34l.com/post/22062.html

分享给朋友:

相关文章

2020年创业做什么项目投资小回报高?

2020年创业做什么项目投资小回报高?

相信很多人都有过创业的想法,有的可能一直处于想象中,从未实施过,有的可能已经在行动了。作为正在想着如何创业的人,如果你是土豪,可以忽略本文,如果不是,我想你有必要看下2020年创业做什么项目投资小回报高?因为这下面的项目介绍对你或许有帮助。…

30句用喜从天降造句的语句

30句用喜从天降造句的语句

1、让他去泰山旅游,那真是喜从天降。…

华盛顿:自己不能胜任的事情,切莫轻易答应别人

华盛顿:自己不能胜任的事情,切莫轻易答应别人

自己不能胜任的事情,切莫轻易答应别人,一旦答应了别人,就必须实践自己的诺言。——华盛顿 这则名言告诉我们什么道理?我们应该怎么做?…

25句励志短句霸气,超励志的语录

25句励志短句霸气,超励志的语录

一、在人生的道路上,从来没有全身而退,坐享其成,不劳而获一说。你不努力,就得出局。…

英国正在调查一种新出现的变异病毒

英国正在调查一种新出现的变异病毒

新华社伦敦3月11日电(记者张家伟 金晶)英国英格兰公共卫生局11日表示,该国近期确诊两例感染了一种新的变异新冠病毒的病例,目前正在对这一新的变异病毒开展调查。…

扫地机器人市场的2021:后浪翻涌,前浪头疼

扫地机器人市场的2021:后浪翻涌,前浪头疼

编者按:本文来自锋见,创业邦经授权发布。 转眼2021年已接近尾声,2022年即将来临。回顾过去这一年,注定是不平凡的一年,特别是对于扫地机器人行业来说,2021年发生了不少大事,对行业影响深远。今天我们就来盘点和点评一下。 科技改变生活,…