前言
在网页在看见小米动态图的实现,最近正在学习kotlin,就准备自己也实现一个,
参考实现(建议先读):
一、实现一个三角形的变化:
1、计算三角形坐标
mLength是高,mStartX,mStartY是开始坐标,offset边长,自己设置开始位置
val offset = (mLength * 2 / Math.sqrt(3f.toDouble())).toFloat()mTriangleEntity.startX = mStartXmTriangleEntity.startY = mStartYmTriangleEntity.endX1 = mStartX - mLengthmTriangleEntity.endX2 = mStartX - mLengthmTriangleEntity.endY1 = mStartY - offset / 2mTriangleEntity.endY2 = mStartY + offset / 2
2、使用drawPath来绘制三角形
override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas ?: return mPath.reset() mPath.moveTo(mTriangleEntity.startX, mTriangleEntity.startY) mPath.lineTo(mTriangleEntity.currentX1, mTriangleEntity.currentY1) mPath.lineTo(mTriangleEntity.currentX2, mTriangleEntity.currentY2) mPaint.color = color1 canvas.drawPath(mPath, mPaint) }
3、使用valueAnimator来生成动态数据,使图形变化
private fun doAnimator() { mValueAnimator.addUpdateListener { val data = it.animatedFraction mTriangleEntity.apply { currentX1 = startX + data * (endX1 - startX) currentX2 = startX + data * (endX2 - startX) currentY1 = startY + data * (endY1 - startY) currentY2 = startY + data * (endY2 - startY) } invalidate() } mValueAnimator.start() }
二、实现多个三角形
基本原理和一个三角形动态变化是一样的,我们只需要初始化好三角形,然后按照顺序进行相应的变化绘制就可以
1、初始化三角形
一共4个三角形,6个点,先定义start点,其他经过计算出来就行
private fun initTriangle() { val offset = (mLength * 2 / Math.sqrt(3f.toDouble())).toFloat() //最右边的点 val point1 = TrianglePoint(mStartX, mStartY) //最上面的点 val point2 = TrianglePoint(mStartX - mLength, mStartY - offset / 2) //最下面的点 val point3 = TrianglePoint(mStartX - mLength, mStartY + offset / 2) val point4 = TrianglePoint(mStartX - mLength / 2, mStartY + offset / 4) val point6 = TrianglePoint(mStartX - mLength / 2, mStartY - offset / 4) val point5 = TrianglePoint(mStartX - mLength, mStartY) mTriangles = arrayOfNulls(4) mTriangles[0] = TriangleEntity(point4, point6, point5, null, null, color1) mTriangles[1] = TriangleEntity(point5, point6, point2, null, null, color2) mTriangles[2] = TriangleEntity(point6, point4, point1, null, null, color3) mTriangles[3] = TriangleEntity(point4, point5, point3, null, null, color4) }
2、执行顺序
用枚举状态来控制执行,设置动画一直执行,在重复回调方法中控制进度
private enum class LoadStatus { MID_LOADING, FIRST_LOADING, SECOND_LOADING, THIRD_LOADING, THIRD_DISMISS, FIRST_DISMISS, SECOND_DISMISS, MID_DISMISS, LOADING_COMPLETE }
mValueAnimator.addListener(object : Animator.AnimatorListener { override fun onAnimationRepeat(animation: Animator?) { LogUtils.e("onAnimationRepeat.....1" + mCurrentStatus.name) mCurrentStatus = when (mCurrentStatus) { LoadStatus.MID_LOADING -> LoadStatus.FIRST_LOADING LoadStatus.FIRST_LOADING -> LoadStatus.SECOND_LOADING LoadStatus.SECOND_LOADING -> LoadStatus.THIRD_LOADING LoadStatus.THIRD_LOADING -> { reverseTriangleStart() LoadStatus.LOADING_COMPLETE } //reverseTriangleStart() LoadStatus.LOADING_COMPLETE -> LoadStatus.THIRD_DISMISS LoadStatus.THIRD_DISMISS -> LoadStatus.FIRST_DISMISS LoadStatus.FIRST_DISMISS -> LoadStatus.SECOND_DISMISS LoadStatus.SECOND_DISMISS -> LoadStatus.MID_DISMISS LoadStatus.MID_DISMISS -> { reverseTriangleStart() LoadStatus.MID_LOADING } } }
3、动态变化
mValueAnimator.addUpdateListener { var data = it.animatedFraction if (mCurrentStatus.ordinal > 3) { data = 1 - data }if (mCurrentStatus != LoadStatus.LOADING_COMPLETE) { mTriangles[mCurrentStatus.ordinal % 4]?.run { currentP1 = getCurrentPoint(data, startP, endP1) currentP2 = getCurrentPoint(data, startP, endP2) } invalidate() } }
注意红色部分,必须要把枚举状态的顺序固定才能这么写,不然顺序执行有问题。
4、绘制
override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas ?: return mTriangles.forEach { mPath.reset() mPath.moveTo(it.startP.x, it.startP.y) mPath.lineTo(it.currentP1!!.x, it.currentP1!!.y) mPath.lineTo(it.currentP2!!.x, it.currentP2!!.y) mPath.close() mPaint.color = it.color canvas.drawPath(mPath, mPaint) if (mCurrentStatus == LoadStatus.MID_LOADING) { return } } }
其实只要熟悉绘制一个三角形的方法,多个只是数学计算和逻辑上的一些控制,难度不大。
主要在中间遇到几个问题:
1、onAnimationRepeat 在重新绘制的时候会调用2次。
主要原因:执行动画在onLayout方法,会导致出现此情况。修改方式:等待显示后在执行动画,正常。(具体原因其实也不清楚,猜测在View绘制的时候就去执行动画,可能会引起绘制的错误)
(后续在研究view的绘制过程中发现,view的onMeasure onLayout至少会执行两次导致)
2、绘制三角形不是完整的,一个三角形可能还看不出,多个时候就比较明显。
主要原因:动画得到data是(0,1)之间的数,导致计算的当前点不等于最终点
解决方法:没找到valueAnimator可用的方法,就在onAnimationRepeat 中把当前点设置成结束点重新绘制一遍。