目录
前言
之前做了一个答题的小游戏,有一个倒计时的效果,因此就写了一个,在此记录一下方便以后使用
实现效果
实现原理
1.首先画一个白色的圆作为背景,然后利用Paint的setShadowLayer方法为圆形背景加一个投影,使其更有立体感。
2.利用Canvas的drawArc方法在这个圆的上方画一个圆环,这里需要将Paint的Style设置为Paint.Style.STROKE
3.用Canvas的drawText方法将倒计时的时间画到控件中心位置
4.最后创建一个ValueAnimator用来改变圆弧的长度和倒计时的数字,从而产生动画效果。
代码展示
/**
* 倒计时控件
*/
class TimeDownView: View {
private var mPaintRing: Paint = Paint(Paint.ANTI_ALIAS_FLAG) //环形的圆的画笔
private var mPaintCircle:Paint = Paint(Paint.ANTI_ALIAS_FLAG)//圆形背景的画笔
private var mPaintTimeText = Paint(Paint.ANTI_ALIAS_FLAG) //时间文字的画笔
private var mShadowSize = 10f//阴影大小
private var mRingWidth = 20f//圆环宽度
private var mAnimator: ValueAnimator? = null
private var mRectfRing: RectF? = null //圆环的矩形
private var mProgress = 10f //进度
private var mAngle = 90F
private var mTextBaseY = 0F//文字居中的位置
var mTimeCountDownCallBack:TimeCountDownCallBack? = null
constructor(context: Context) : super(context){
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
{
init()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr){
init()
}
private fun init(){
setLayerType(LAYER_TYPE_SOFTWARE,null)//关闭硬件加速
mPaintCircle.color = Color.WHITE
mPaintCircle.style = Paint.Style.FILL
mPaintCircle.setShadowLayer(mShadowSize,0F,0F,Color.BLACK)
mPaintRing.color = Color.BLUE
mPaintRing.style = Paint.Style.STROKE
mPaintRing.strokeWidth = mRingWidth
mPaintTimeText.color = Color.BLACK
mPaintTimeText.textAlign = Paint.Align.CENTER
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mRingWidth = width / 10F
mPaintTimeText.strokeWidth = mRingWidth
mPaintTimeText.textSize = (width - mShadowSize - mRingWidth)/2
val ringMargen = mRingWidth/3*2
mRectfRing = RectF(ringMargen+mShadowSize,ringMargen+mShadowSize,width-ringMargen-mShadowSize,height-ringMargen-mShadowSize)
val fontMetrics = mPaintTimeText.fontMetrics
// 计算文字高度
val fontHeight = fontMetrics.bottom - fontMetrics.top
// 计算文字baseline 让文字垂直居中
mTextBaseY = height - (height - fontHeight) / 2 - fontMetrics.bottom
startAnim()
}
/**
* 开始动画
*/
fun startAnim(){
if(mAnimator!=null){
mAnimator!!.cancel()
}
mAnimator = ValueAnimator.ofFloat(11f, 1f)
mAnimator!!.duration = 10000L
//设置动画差值器,让动画为匀速动画
mAnimator!!.interpolator = LinearInterpolator()
mAnimator!!.addUpdateListener {
mProgress = it.animatedValue as Float
invalidate()
}
mAnimator!!.repeatMode = ValueAnimator.RESTART
mAnimator!!.repeatCount = ValueAnimator.INFINITE
mAnimator!!.addListener(object : AnimatorListenerAdapter(){
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
}
override fun onAnimationRepeat(animation: Animator?) {
super.onAnimationRepeat(animation)
//回调倒计时重复的事件
if(mTimeCountDownCallBack!=null){
mTimeCountDownCallBack!!.onRepeat()
}
}
})
mAnimator!!.start()
}
/**
* 结束动画
*/
fun stopAnim(){
if(mAnimator!=null){
mAnimator!!.cancel()
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
mAnimator?.cancel()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val centerX:Float = (width / 2).toFloat()
val centerY:Float = (height / 2).toFloat()
// 画圆形背景
canvas!!.drawCircle(centerX,centerY,Math.min(centerX,centerY)-mShadowSize,mPaintCircle)
//画圆环
canvas.drawArc(mRectfRing!!,0F - mAngle,-36F*(mProgress-1),false,mPaintRing)
canvas.drawText("${mProgress.toInt()}",centerX,mTextBaseY,mPaintTimeText)
}
/**
* 倒计时结束的时候的回调
*/
interface TimeCountDownCallBack{
fun onRepeat()
}
}