Android 实现水波纹动效
WaterRippleView核心文件
package com.manss.myapplication.widget.animation
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 com.manss.myapplication.widget.DisplayUtil
import java.util.*
/**
* 水波纹动画view
*/
class WaterRippleView : View {
private var mMaxWaveAreaRadius = 0f
private var mWaveIntervalSize = 0f//波距 = 0f
private var mStirStep = 0f // 波移动的步幅 = 0f
private var mWidth = 0
private var mWaveStartWidth = 0f// px = 0f
private var mWaveEndWidth = 0f// px 最大半径,超过波消失 = 0f
private var mWaveColor = 0
private var mViewCenterX = 0f
private var mViewCenterY = 0f
private val rippleColor = Color.BLUE
//波动属性设置
private val mWavePaint = Paint()
//中心点属性设置
private val mWaveCenterShapePaint = Paint()
private var mFillAllView = false
private var mFillWaveSourceShapeRadius = 0f
private val mWaves: MutableList = ArrayList()
constructor(context: Context?, attrs: AttributeSet?) : super(
context,
attrs
) {
init()
}
constructor(context: Context?) : super(context) {
init()
}
private fun init() {
setWaveInfo(2f, 1f, 2f, 15f, rippleColor)
mWaveIntervalSize = DisplayUtil.dip2px(context, 20f).toFloat()
mWidth = DisplayUtil.dip2px(context, 2f)
//初始化波动最大半径
mWaveEndWidth = DisplayUtil.dip2px(context, 100f).toFloat()
}
override fun onLayout(
changed: Boolean, left: Int, top: Int, right: Int,
bottom: Int
) {
super.onLayout(changed, left, top, right, bottom)
mViewCenterX = width / 2.toFloat()
mViewCenterY = height / 2.toFloat()
var waveAreaRadius = mMaxWaveAreaRadius
waveAreaRadius = if (mFillAllView) {
Math.sqrt(
(mViewCenterX * mViewCenterX
+ mViewCenterY * mViewCenterY).toDouble()
).toFloat()
} else {
Math.min(mViewCenterX, mViewCenterY)
}
if (mMaxWaveAreaRadius != waveAreaRadius) {
mMaxWaveAreaRadius = waveAreaRadius
resetWave()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
stir()
for (w in mWaves) {
mWavePaint.color = w!!.color
mWavePaint.strokeWidth = mWidth.toFloat()
mWavePaint.alpha = w.alpha
canvas.drawCircle(mViewCenterX, mViewCenterY, w.radius, mWavePaint)
}
if (mFillWaveSourceShapeRadius > 0f) {
canvas.drawCircle(
mViewCenterX, mViewCenterY,
mFillWaveSourceShapeRadius, mWaveCenterShapePaint
)
}
postInvalidateDelayed(FPS.toLong())
}
/**
* 波
*
*/
internal inner class Wave {
var radius = 0f
var width = 0f
var color = 0
var alpha = 0
fun reset() {
radius = 0f
width = mWaveStartWidth
color = mWaveColor
}
override fun toString(): String {
return ("Wave [radius=" + radius + ", width=" + width + ", color="
+ color + "]")
}
init {
reset()
}
}
private var mLastRmoveWave: Wave? = null
/**
* 触发涌动传播
*/
private fun stir() {
val nearestWave = if (mWaves.isEmpty()) null else mWaves[0]
if (nearestWave == null || nearestWave.radius >= mWaveIntervalSize) {
var w: Wave? = null
if (mLastRmoveWave != null) {
w = mLastRmoveWave
mLastRmoveWave = null
w!!.reset()
} else {
w = Wave()
}
mWaves.add(0, w)
}
val waveWidthIncrease = mWaveEndWidth - mWaveStartWidth
val size = mWaves.size
for (i in 0 until size) {
val w = mWaves[i]
var rP = w!!.radius / mMaxWaveAreaRadius
if (rP > 1f) {
rP = 1f
}
w.width = mWaveStartWidth + rP * waveWidthIncrease
w.radius += mStirStep
w.color = rippleColor
w.alpha = 255 / (i + 1)
}
val farthestWave = mWaves[size - 1]
if (farthestWave!!.radius > mWaveEndWidth) {
mWaves.removeAt(size - 1)
}
}
/**
* 如果true会选择view的最大的对角线作为活动半径
*
*/
fun setFillAllView(fillAllView: Boolean) {
mFillAllView = fillAllView
resetWave()
}
fun resetWave() {
mWaves.clear()
postInvalidate()
}
/**
* 填充波形起源的中心点
*
* @param radius 半径大小
*/
fun setFillWaveSourceShapeRadius(radius: Float) {
mFillWaveSourceShapeRadius = radius
}
/**
* 设置波形属性
*
* @param intervalSize 两个波形之间的间距
* @param stireStep 波形移动速度
* @param startWidth 起始波形宽度
* @param endWidth 终止波形宽度
* @param color 波形颜色
*/
fun setWaveInfo(
intervalSize: Float, stireStep: Float,
startWidth: Float, endWidth: Float, color: Int
) {
mWaveIntervalSize = intervalSize
mStirStep = stireStep
mWaveStartWidth = startWidth
mWaveEndWidth = endWidth
setWaveColor(color)
resetWave()
}
/**
* 设置波形颜色
*
* @param color
*/
fun setWaveColor(color: Int) {
mWaveColor = color
mWaveCenterShapePaint.color = mWaveColor
}
companion object {
private const val FPS = 1000 / 40
}
init {
mWavePaint.isAntiAlias = true
mWavePaint.style = Paint.Style.FILL
}
init {
mWaveCenterShapePaint.isAntiAlias = true
mWaveCenterShapePaint.style = Paint.Style.FILL
}
}
util文件
package com.manss.myapplication.widget;
import android.content.Context;
public class DisplayUtil {
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
}
activity_water_ripple_view.xml文件
方法调用
package com.example.myapplication
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.manss.myapplication.R
import com.manss.myapplication.widget.DisplayUtil
import com.manss.myapplication.widget.animation.WaterRippleView
/**
* 水波纹动画
*/
class WaterRippleActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_water)
var waterRipple: WaterRippleView = findViewById(R.id.activity_water_ripple_view)
waterRipple.setFillWaveSourceShapeRadius(DisplayUtil.dip2px(this,15f).toFloat())
// waterRipple.setFillAllView(true)
// waterRipple.setWaveColor(android.R.color.holo_blue_bright)
}
}
总结,如果波纹是线条效果,修改WaterRippleView文件的
init {
mWavePaint.isAntiAlias = true
mWavePaint.style = Paint.Style.FILL
}
修改后
init {
mWavePaint.isAntiAlias = true
mWavePaint.style = Paint.Style.STROKE
}
修改波纹半径
//初始化波动最大半径
mWaveEndWidth = DisplayUtil.dip2px(context, 100f).toFloat()
修改波纹颜色
private val rippleColor = Color.BLUE
//波动属性设置