最终效果
实现思路
View绘制
整体是一个组合View,我们可以通过继承RelativeLayout
(因为中间圆点需要居中),往里面添加三个圆点来实现;
动画效果
一开始三个圆点均处于RelativeLayout
中间,左右两个圆点需要平移到两侧,平移结束后再平移到中间位置,此时需要改变圆点颜色,中间平移结束后再次左右平移,如此反复,即可实现最终效果。
相关源码
- 首先,自定义一个
CircleView
,用于表示每个圆点
package com.crystal.view.animation
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
/**
* 圆点
* on 2022/11/9
*/
class CircleView : View {
/**
* 画笔工具
*/
private var paint: Paint = Paint()
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
paint.isAntiAlias = true
paint.isDither = true
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//设置宽高相等
var widthSize = MeasureSpec.getSize(widthMeasureSpec)
var heightSize = MeasureSpec.getSize(heightMeasureSpec)
if (widthSize > heightSize) {
widthSize = heightSize
} else {
heightSize = widthSize
}
setMeasuredDimension(widthSize, heightSize)
}
override fun onDraw(canvas: Canvas) {
//绘制圆
canvas.drawCircle(width / 2f, width / 2f, width / 2f, paint)
}
/**
* 为圆形渲染上颜色
*/
fun renderCircleColor(color: Int) {
paint.color = color
invalidate()
}
/**
* 获取渲染颜色
*/
fun getRenderColor(): Int {
return paint.color
}
}
- 自定义数据加载动画
ExChangeLoadingView
package com.crystal.view.animation
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.util.TypedValue
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.widget.RelativeLayout
/**
* 圆点加载动画
* on 2022/11/9
*/
class ExChangeLoadingView : RelativeLayout {
/**
* 左侧圆点
*/
private lateinit var leftCircleView: CircleView
/**
* 中间圆点
*/
private lateinit var middleCircleView: CircleView
/**
* 右侧圆点
*/
private lateinit var rightCircleView: CircleView
/**
* 左右运动的距离
*/
private var distanceX = dp2px(40).toFloat()
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
) {
//在view测量之后进行绘制
post {
initViews()
//左侧和右侧圆点分别向两侧运动
outerAnimation()
}
}
/**
* 左右圆点向两侧运动
*/
private fun outerAnimation() {
val leftAnimator = ObjectAnimator.ofFloat(leftCircleView, "translationX", 0f, -distanceX)
val rightAnimator = ObjectAnimator.ofFloat(rightCircleView, "translationX", 0f, distanceX)
val animatorSet = AnimatorSet()
//设置加速差值器
animatorSet.interpolator = AccelerateInterpolator()
animatorSet.duration = 500L
animatorSet.playTogether(leftAnimator, rightAnimator)
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
//动画结束后,左右圆点向中间运动
innerAnimation()
}
})
animatorSet.start()
}
/**
* 左右圆点向中心运动
*/
private fun innerAnimation() {
val leftAnimator = ObjectAnimator.ofFloat(leftCircleView, "translationX", -distanceX, 0f)
val rightAnimator = ObjectAnimator.ofFloat(rightCircleView, "translationX", distanceX, 0f)
val animatorSet = AnimatorSet()
//设置减速差值器
animatorSet.interpolator = DecelerateInterpolator()
animatorSet.duration = 500L
animatorSet.playTogether(leftAnimator, rightAnimator)
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
//动画结束后,左右圆点向中间运动
outerAnimation()
exChangeCircleColor()
}
})
animatorSet.start()
}
private fun initViews() {
//添加三个View
val layoutParams = LayoutParams(dp2px(10), dp2px(10))
layoutParams.addRule(CENTER_IN_PARENT)
leftCircleView = CircleView(context)
leftCircleView.renderCircleColor(Color.BLUE)
leftCircleView.layoutParams = layoutParams
addView(leftCircleView)
rightCircleView = CircleView(context)
rightCircleView.renderCircleColor(Color.RED)
rightCircleView.layoutParams = layoutParams
addView(rightCircleView)
middleCircleView = CircleView(context)
middleCircleView.renderCircleColor(Color.GREEN)
middleCircleView.layoutParams = layoutParams
//中间圆形最后添加覆盖在另两个之上
addView(middleCircleView)
}
private fun dp2px(dp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
resources.displayMetrics
).toInt()
}
/**
* 进行三个圆形颜色变化 左边的颜色给中间 中间的给右边 右边的颜色给左边
*/
private fun exChangeCircleColor() {
val leftColor = leftCircleView.getRenderColor()
val middleColor = middleCircleView.getRenderColor()
val rightColor = rightCircleView.getRenderColor()
middleCircleView.renderCircleColor(leftColor)
rightCircleView.renderCircleColor(middleColor)
leftCircleView.renderCircleColor(rightColor)
}
}
总结
看似复杂的效果,其实将效果一步步进行拆分后,实际实现的代码竟如此简单。
结语
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )