Android自定义View实现时钟功能
最近在练习自定义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元,如下图所示:







