先看一下要實現(xiàn)的效果
從上面的動畫分析品擎,可以分為以下幾步
- 六個圓圍繞圓心旋轉(zhuǎn)。
- 旋轉(zhuǎn)完成后备徐,先進(jìn)行擴(kuò)散萄传,后聚合。
- 聚合完成后蜜猾,從圓心慢慢擴(kuò)散秀菱,展示后面的視圖。
第一步畫六個圓
package rc.loveq.splashview
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import kotlin.math.hypot
import kotlin.properties.Delegates
import kotlin.math.cos
import kotlin.math.sin
/**
* Author:Rc
* 0n 2019/8/26 22:42
*/
class SplashView(context: Context, attrs: AttributeSet) : View(context, attrs) {
// 背景色
private val bgColor = Color.WHITE
// 6個圓的顏色
private val circleColor: IntArray = context.resources.getIntArray(R.array.splash_circle_colors)
// 6個圓畫筆
private var paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
//View中心點的x坐標(biāo)
var centerX by Delegates.notNull<Float>()
//View中心點的y坐標(biāo)
var centerY by Delegates.notNull<Float>()
//當(dāng)前View對角線的一半
var distance by Delegates.notNull<Float>()
private val rotateRadius = 90f
// 默認(rèn)六個圓圍繞的外圍圓半徑
private val currentRotateRadius = rotateRadius
// 6個圓半徑
private val circleRadius = 18f
// 當(dāng)前splash的狀態(tài)
private var splashState: SplashState? = null
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
//獲取中心點
centerX = (w / 2).toFloat()
centerY = (h / 2).toFloat()
//當(dāng)前View對角線的一半
distance = (hypot(x.toDouble(), h.toDouble()) / 2).toFloat()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//初始化默認(rèn)狀態(tài)
splashState ?: splashState.apply {
splashState = RotateStatus()
}
splashState!!.drawState(canvas)
}
/**
* 閃屏頁的狀態(tài)的抽象類
*/
abstract inner class SplashState {
abstract fun drawState(canvas: Canvas)
}
/**
* 1.旋轉(zhuǎn)
*/
inner class RotateStatus : SplashState() {
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
drawCircle(canvas)
}
private fun drawCircle(canvas: Canvas) {
//每個圓的弧度
val rotateAngle = (Math.PI * 2 / circleColor.size)
// 根據(jù)6種顏色蹭睡,獲取6個球
for (i in circleColor.indices) {
// 獲取第i個圓的角度 衍菱;如果旋轉(zhuǎn)的話需要加上角度
val angle = i * rotateAngle
// x = r * cos(a) + centerX
// y = r * sin(a) + centerY
val cx = (currentRotateRadius * cos(angle) + centerX).toFloat()
val cy = (currentRotateRadius * sin(angle) + centerY).toFloat()
paint.color = circleColor[i]
canvas.drawCircle(cx, cy, circleRadius, paint)
}
}
private fun drawBackground(canvas: Canvas) {
//畫背景色
canvas.drawColor(bgColor)
}
}
}
第二步讓六個圓動起來
package rc.loveq.splashview
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import kotlin.math.hypot
import kotlin.properties.Delegates
import kotlin.math.cos
import kotlin.math.sin
/**
* Author:Rc
* 0n 2019/8/26 22:42
*/
class SplashView(context: Context, attrs: AttributeSet) : View(context, attrs) {
// 背景色
private val bgColor = Color.WHITE
// 6個圓的顏色
private val circleColor: IntArray = context.resources.getIntArray(R.array.splash_circle_colors)
// 6個圓畫筆
private var paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
//View中心點的x坐標(biāo)
var centerX by Delegates.notNull<Float>()
//View中心點的y坐標(biāo)
var centerY by Delegates.notNull<Float>()
//當(dāng)前View對角線的一半
var distance by Delegates.notNull<Float>()
private val rotateRadius = 90f
// 默認(rèn)六個圓圍繞的外圍圓半徑
private val currentRotateRadius = rotateRadius
// 6個圓半徑
private val circleRadius = 18f
// 當(dāng)前splash的狀態(tài)
private var splashState: SplashState? = null
// 旋轉(zhuǎn)動畫時長
private val rotateDuration = 1200
// 當(dāng)前大圓的旋轉(zhuǎn)角度
private var currentRotateAngle = 0f
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
//獲取中心點
centerX = (w / 2).toFloat()
centerY = (h / 2).toFloat()
//當(dāng)前View對角線的一半
distance = (hypot(x.toDouble(), h.toDouble()) / 2).toFloat()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//初始化默認(rèn)狀態(tài)
splashState ?: splashState.apply {
splashState = RotateStatus()
}
splashState!!.drawState(canvas)
}
/**
* 閃屏頁的狀態(tài)的抽象類
*/
abstract inner class SplashState {
abstract fun drawState(canvas: Canvas)
}
/**
* 1.旋轉(zhuǎn)
*/
inner class RotateStatus : SplashState() {
init {
val valueAnimator = ValueAnimator.ofFloat(0f, (Math.PI * 2).toFloat())
.apply {
repeatCount = 2
duration = rotateDuration.toLong()
interpolator = LinearInterpolator()
}
valueAnimator.addUpdateListener {
currentRotateAngle = it.animatedValue as Float
invalidate()
}
valueAnimator.start()
}
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
drawCircle(canvas)
}
private fun drawCircle(canvas: Canvas) {
//每個圓的弧度
val rotateAngle = (Math.PI * 2 / circleColor.size)
// 根據(jù)6種顏色,獲取6個球
for (i in circleColor.indices) {
// 獲取第i個圓的角度 肩豁;如果旋轉(zhuǎn)的話需要加上角度
val angle = i * rotateAngle + currentRotateAngle
// x = r * cos(a) + centerX
// y = r * sin(a) + centerY
val cx = (currentRotateRadius * cos(angle) + centerX).toFloat()
val cy = (currentRotateRadius * sin(angle) + centerY).toFloat()
paint.color = circleColor[i]
canvas.drawCircle(cx, cy, circleRadius, paint)
}
}
private fun drawBackground(canvas: Canvas) {
//畫背景色
canvas.drawColor(bgColor)
}
}
}
上面的代碼中脊串,在RotateStatus
構(gòu)造函數(shù)中辫呻,使用ValueAnimator
來動態(tài)更新每個圓的角度,從而使六個圓動起來琼锋。
第三步實現(xiàn)聚合效果
package rc.loveq.splashview
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.animation.LinearInterpolator
import android.view.animation.OvershootInterpolator
import androidx.core.animation.addListener
import kotlin.math.hypot
import kotlin.properties.Delegates
import kotlin.math.cos
import kotlin.math.sin
/**
* Author:Rc
* 0n 2019/8/26 22:42
*/
class SplashView(context: Context, attrs: AttributeSet) : View(context, attrs) {
// 背景色
private val bgColor = Color.WHITE
// 6個圓的顏色
private val circleColor: IntArray = context.resources.getIntArray(R.array.splash_circle_colors)
// 6個圓畫筆
private var paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
//View中心點的x坐標(biāo)
var centerX by Delegates.notNull<Float>()
//View中心點的y坐標(biāo)
var centerY by Delegates.notNull<Float>()
//當(dāng)前View對角線的一半
var distance by Delegates.notNull<Float>()
private val rotateRadius = 90f
// 默認(rèn)六個圓圍繞的外圍圓半徑
private var currentRotateRadius = rotateRadius
// 6個圓半徑
private val circleRadius = 18f
// 當(dāng)前splash的狀態(tài)
private var splashState: SplashState? = null
// 旋轉(zhuǎn)動畫時長
private val rotateDuration = 1200
// 當(dāng)前大圓的旋轉(zhuǎn)角度
private var currentRotateAngle = 0f
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
//獲取中心點
centerX = (w / 2).toFloat()
centerY = (h / 2).toFloat()
//當(dāng)前View對角線的一半
distance = (hypot(x.toDouble(), h.toDouble()) / 2).toFloat()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//初始化默認(rèn)狀態(tài)
splashState ?: splashState.apply {
splashState = RotateStatus()
}
splashState!!.drawState(canvas)
}
/**
* 閃屏頁的狀態(tài)的抽象類
*/
abstract inner class SplashState {
abstract fun drawState(canvas: Canvas)
}
/**
* 1.旋轉(zhuǎn)
*/
inner class RotateStatus : SplashState() {
init {
val valueAnimator = ValueAnimator.ofFloat(0f, (Math.PI * 2).toFloat())
.apply {
repeatCount = 2
duration = rotateDuration.toLong()
interpolator = LinearInterpolator()
}
valueAnimator.addUpdateListener {
currentRotateAngle = it.animatedValue as Float
invalidate()
}
valueAnimator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
splashState = MerginStatus()
}
})
valueAnimator.start()
}
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
drawCircle(canvas)
}
}
/**
* 2.聚合擴(kuò)散
*/
inner class MerginStatus : SplashState() {
init {
val valueAnimator = ValueAnimator.ofFloat(circleRadius, rotateRadius)
.apply {
duration = rotateDuration.toLong()
interpolator = OvershootInterpolator()
}
valueAnimator.addUpdateListener {
currentRotateRadius = it.animatedValue as Float
invalidate()
}
//這里沒有使用start,而是使用reverse
//reverse 會從ValueAnimator end的地方開始放闺,應(yīng)用OvershootInterpolator進(jìn)行值的更新
valueAnimator.reverse()
}
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
drawCircle(canvas)
}
}
private fun drawCircle(canvas: Canvas) {
//每個圓的弧度
val rotateAngle = (Math.PI * 2 / circleColor.size)
// 根據(jù)6種顏色,獲取6個球
for (i in circleColor.indices) {
// 獲取第i個圓的角度 缕坎;如果旋轉(zhuǎn)的話需要加上角度
val angle = i * rotateAngle + currentRotateAngle
// x = r * cos(a) + centerX
// y = r * sin(a) + centerY
val cx = (currentRotateRadius * cos(angle) + centerX).toFloat()
val cy = (currentRotateRadius * sin(angle) + centerY).toFloat()
paint.color = circleColor[i]
canvas.drawCircle(cx, cy, circleRadius, paint)
}
}
private fun drawBackground(canvas: Canvas) {
//畫背景色
canvas.drawColor(bgColor)
}
}
旋轉(zhuǎn)完成后怖侦,進(jìn)行聚合動畫,動畫使用的差值器是OvershootInterpolator
谜叹,這個差值器是有彈性回彈的效果匾寝。
第四步擴(kuò)展
package rc.loveq.splashview
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.animation.LinearInterpolator
import android.view.animation.OvershootInterpolator
import androidx.core.animation.addListener
import kotlin.math.hypot
import kotlin.properties.Delegates
import kotlin.math.cos
import kotlin.math.sin
import android.icu.lang.UCharacter.GraphemeClusterBreak.T
/**
* Author:Rc
* 0n 2019/8/26 22:42
*/
class SplashView(context: Context, attrs: AttributeSet) : View(context, attrs) {
// 背景色
private val bgColor = Color.WHITE
// 6個圓的顏色
private val circleColor: IntArray = context.resources.getIntArray(R.array.splash_circle_colors)
// 6個圓畫筆
private var paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
//View中心點的x坐標(biāo)
var centerX by Delegates.notNull<Float>()
//View中心點的y坐標(biāo)
var centerY by Delegates.notNull<Float>()
//當(dāng)前View對角線的一半
var distance by Delegates.notNull<Float>()
private val rotateRadius = 90f
// 默認(rèn)六個圓圍繞的外圍圓半徑
private var currentRotateRadius = rotateRadius
// 6個圓半徑
private val circleRadius = 18f
// 當(dāng)前splash的狀態(tài)
private var splashState: SplashState? = null
// 旋轉(zhuǎn)動畫時長
private val rotateDuration = 1200
// 當(dāng)前大圓的旋轉(zhuǎn)角度
private var currentRotateAngle = 0f
// 擴(kuò)散圓的半徑
private var currentHoleRadius = 0f
private var holePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
.apply {
style = Paint.Style.STROKE
color = bgColor
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
//獲取中心點
centerX = (w / 2).toFloat()
centerY = (h / 2).toFloat()
//當(dāng)前View對角線的一半
distance = (hypot(w.toDouble(), h.toDouble()) / 2f).toFloat()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//初始化默認(rèn)狀態(tài)
splashState ?: splashState.apply {
splashState = RotateStatus()
}
splashState!!.drawState(canvas)
}
/**
* 閃屏頁的狀態(tài)的抽象類
*/
abstract inner class SplashState {
abstract fun drawState(canvas: Canvas)
}
/**
* 1.旋轉(zhuǎn)
*/
inner class RotateStatus : SplashState() {
init {
val valueAnimator = ValueAnimator.ofFloat(0f, (Math.PI * 2).toFloat())
.apply {
repeatCount = 1
duration = rotateDuration.toLong()
interpolator = LinearInterpolator()
}
valueAnimator.addUpdateListener {
currentRotateAngle = it.animatedValue as Float
invalidate()
}
valueAnimator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
splashState = MerginStatus()
}
})
valueAnimator.start()
}
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
drawCircle(canvas)
}
}
/**
* 2.聚合擴(kuò)散
*/
inner class MerginStatus : SplashState() {
init {
val valueAnimator = ValueAnimator.ofFloat(circleRadius, rotateRadius)
.apply {
duration = rotateDuration.toLong()
interpolator = OvershootInterpolator()
}
valueAnimator.addUpdateListener {
currentRotateRadius = it.animatedValue as Float
invalidate()
}
valueAnimator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
splashState = ExpandState()
}
})
//這里沒有使用start,而是使用reverse
//reverse 會從ValueAnimator end的地方開始,應(yīng)用OvershootInterpolator進(jìn)行值的更新
valueAnimator.reverse()
}
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
drawCircle(canvas)
}
}
/**
* 3.擴(kuò)展
*/
inner class ExpandState : SplashState() {
init {
val valueAnimator = ValueAnimator.ofFloat(circleRadius, distance)
.apply {
duration = rotateDuration.toLong()
interpolator = LinearInterpolator()
}
valueAnimator.addUpdateListener {
currentHoleRadius = it.animatedValue as Float
invalidate()
}
valueAnimator.start()
}
override fun drawState(canvas: Canvas) {
drawBackground(canvas)
}
}
private fun drawCircle(canvas: Canvas) {
//每個圓的弧度
val rotateAngle = (Math.PI * 2 / circleColor.size)
// 根據(jù)6種顏色荷腊,獲取6個球
for (i in circleColor.indices) {
// 獲取第i個圓的角度 旗吁;如果旋轉(zhuǎn)的話需要加上角度
val angle = i * rotateAngle + currentRotateAngle
// x = r * cos(a) + centerX
// y = r * sin(a) + centerY
val cx = (currentRotateRadius * cos(angle) + centerX).toFloat()
val cy = (currentRotateRadius * sin(angle) + centerY).toFloat()
paint.color = circleColor[i]
canvas.drawCircle(cx, cy, circleRadius, paint)
}
}
private fun drawBackground(canvas: Canvas) {
if (currentHoleRadius > 0) {
//繪制空心圓
val strokeWidth = distance - currentHoleRadius
val radius = strokeWidth / 2 + currentHoleRadius
holePaint.strokeWidth = strokeWidth
canvas.drawCircle(centerX, centerY, radius, holePaint)
} else {
//畫背景色
canvas.drawColor(bgColor)
}
}
}
水波的效果,就是通過畫空心圓實現(xiàn)停局,不斷增加空心圓的半徑,減少畫筆的寬度香府。