抖音和微视的关注动画差不多,大同小异,话不多说,直接上效果图

4b7aeddb80ba

实际效果图

动画组成:

1.点击关注的时候,开始会有一个放大的动画

2.关注成功☑️的动画

3.缩放消失的动画

核心实现方式:

1.自定义View

2.PathMeasure和PathEffect

代码如下:

class FollowAnimationButton : View {

private var rectRadius = 0f

private var rect: RectF = RectF()

private var rectPaint: Paint = Paint()

private var plusPaint: Paint = Paint()

private var plusLineWidth = 0f

private var plusPath = Path()

private var okPaint = Paint()

private var okLineWidth = 0f

private var okPath = Path()

private var pathMeasure: PathMeasure? = null

private var startDrawOk = false

private var animatorDrawOk: ValueAnimator? = null

private var effect: PathEffect? = null

private var isAnimating = false

constructor(context: Context) : super(context) {

init(context)

}

constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {

init(context)

}

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {

init(context)

}

private fun init(context: Context) {

rectRadius = CommonUtil.dip2px(context, 15f).toFloat()

plusLineWidth = CommonUtil.dip2px(context, 2f).toFloat()

rectPaint.isAntiAlias = true

rectPaint.color = 0xFFEE0051.toInt()

rectPaint.style = Paint.Style.FILL

plusPaint.isAntiAlias = true

plusPaint.color = Color.WHITE

plusPaint.style = Paint.Style.STROKE

plusPaint.strokeWidth = plusLineWidth

okLineWidth = CommonUtil.dip2px(context, 2.5f).toFloat()

okPaint.isAntiAlias = true

okPaint.color = 0xFFEE0051.toInt()

okPaint.style = Paint.Style.STROKE

okPaint.strokeWidth = okLineWidth

}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {

super.onSizeChanged(w, h, oldw, oldh)

val plusSize = measuredHeight * 0.6f

plusPath.moveTo(measuredWidth / 2f, (measuredHeight - plusSize) / 2)

plusPath.lineTo(measuredWidth / 2f, (measuredHeight + plusSize) / 2)

plusPath.moveTo((measuredWidth - plusSize) / 2, measuredHeight / 2f)

plusPath.lineTo((measuredWidth + plusSize) / 2, measuredHeight / 2f)

okPath.moveTo(measuredWidth * 0.31f, measuredHeight * 0.5f)

okPath.lineTo(measuredWidth * 0.45f, measuredHeight * 0.75f)

okPath.lineTo(measuredWidth * 0.68f, measuredHeight * 0.25f)

pathMeasure = PathMeasure(okPath, true)

}

override fun onDraw(canvas: Canvas) {

rect.apply {

left = 0f

top = 0f

right = measuredWidth.toFloat()

bottom = measuredHeight.toFloat()

}

//画勾

if (startDrawOk) {

rectPaint.color = Color.WHITE

canvas.drawRoundRect(rect, rectRadius, rectRadius, rectPaint)

canvas.drawPath(okPath, okPaint)

} else {

rectPaint.color = 0xFFEE0051.toInt()

canvas.drawRoundRect(rect, rectRadius, rectRadius, rectPaint)

//加号

canvas.drawPath(plusPath, plusPaint)

}

}

fun startFollowAnimation(animationEnd: () -> Unit) {

isAnimating = true

val startScaleAnimation = ScaleAnimation(1f, 1.2f, 1f, 1.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)

startScaleAnimation.duration = 300

startScaleAnimation.setAnimationListener(object : AnimationListenerAdapter() {

override fun onAnimationEnd(animation: Animation?) {

super.onAnimationEnd(animation)

startDrawOk = true

animatorDrawOk = ValueAnimator.ofFloat(1f, 0f).apply {

duration = 1000

addUpdateListener {

val value = it.animatedValue as Float

effect = DashPathEffect(floatArrayOf(pathMeasure!!.length, pathMeasure!!.length), value * pathMeasure!!.length)

okPaint.pathEffect = effect

invalidate()

}

addListener(object : AnimatorListenerAdapter() {

override fun onAnimationEnd(animation: Animator?) {

super.onAnimationEnd(animation)

val animationSet = AnimationSet(true)

val alphaAnimation = AlphaAnimation(1f, 0.2f)

val scaleAnimation = ScaleAnimation(1f, 0.2f, 1f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)

animationSet.addAnimation(alphaAnimation)

animationSet.addAnimation(scaleAnimation)

animationSet.duration = 300

animationSet.interpolator = AccelerateInterpolator()

animationSet.setAnimationListener(object : AnimationListenerAdapter() {

override fun onAnimationEnd(animation: Animation?) {

super.onAnimationEnd(animation)

startDrawOk = false

isAnimating = false

animationEnd()

}

})

startAnimation(animationSet)

}

})

start()

}

}

})

startAnimation(startScaleAnimation)

}

fun isAnimating(): Boolean {

return isAnimating

}

}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐