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

Android自定义View实现时钟功能

34资源网2022-09-13284

最近在练习自定义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

分享给朋友:

相关文章

单身想找个女朋友,男的去哪里可以找个女朋友

单身想找个女朋友,男的去哪里可以找个女朋友

现在中国的男女比例失调,男的光棍要比女的多出3000w以上,这是个什么概念?代表着有3000w人是找不到对象的。所以很多单身男的就开始发愁了,单身想找个女朋友究竟到哪里找呢?说实话,小编也是一名单身汉,也正在找女朋友,虽然说,我没有找到女朋…

抖音如果让我遇见你而你正当年轻是什么歌曲?

抖音如果让我遇见你而你正当年轻是什么歌曲?

《抖音》短视频平台上有不少老歌经过翻唱火了,可能刚好歌词传递的情感引起了网友们的共鸣,而最近比较火的一首歌歌词大概是如果让我遇见你而你正当年轻,好多网友不知道首是什么歌曲?小编刚开始也不知道,后来经过搜索得知这是一首老歌《怨苍天变了心》,是…

应用汇下载安装最新版(2021最新版应用汇app)

应用汇下载安装最新版(2021最新版应用汇app)

Donews8月17日消息(记者 邱慧)近日,为安卓手机服务的应用型软件“应用汇”新版正式上线,同时推出“应用收藏”功能——“应用集”。 应用集主要分为“推荐”、“最热”、“最新”三类。应用汇官方介绍,此次新版上线后,安装包被优化缩小,给…

购买须知模板怎么编辑(淘宝买家须知免费素材)

购买须知模板怎么编辑(淘宝买家须知免费素材)

为了帮助您入门,在您注册 Shopify 帐户时,后台的模版页面中会设置一个默认模版。如果您想为在线商店自定义一个不同的模版,则需要向后台添加一个模版。 您可通过以下几种方式添加模版: 如果您的计算机上的 .zip 文件中已有一个模版,那么…

视频号灰度测试购物车功能,微信要扛起腾讯电商大旗了?

视频号灰度测试购物车功能,微信要扛起腾讯电商大旗了?

图源:摄图网 编者按:本文来自微信公众号小谦笔记(ID:xiaoqianshuo),创业邦经授权转载 据自媒体百准报道,除了直播,微信视频号目前正在灰度测试短视频购物车功能,这被视为微信加码电商业务的一大跨步。 早在2014年,腾讯就在微信…

已覆盖70%前十大快递/快运客户,商用车后市场玩家「大车队长」眼中的轮胎“生命力”

已覆盖70%前十大快递/快运客户,商用车后市场玩家「大车队长」眼中的轮胎“生命力”

2020年,商用车后市场头部创业公司「大车队长」正式完成了数千万元人民币A轮融资,由经纬中国领投。融资后的一年里,大车队长成长迅速,还发布了全新的“5113”战略,即5年服务100万台车、1000万个轮位、完成300亿元营收。 截至目前,大…