效果图:
代码:
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import com.blankj.utilcode.util.ConvertUtils
import kotlin.math.min
import kotlin.math.roundToInt
class WaveView(context: Context, attributeSet: AttributeSet?,defStyleAttr : Int) : View(context, attributeSet,defStyleAttr) {
constructor(context: Context) : this(context,null,0)
constructor(context: Context, attributeSet: AttributeSet) : this(context,attributeSet,0)
private var ringPaint = Paint()
private var circlePaint = Paint()
private var wavePaint = Paint()
private var wavePath = Path()
private lateinit var ringRectF : RectF
private var circlePath = Path()
/**
* 波长
*/
private var waveLength = 0
private var offset = 0
/**
* 当前高度
*/
private var curHeight = 0
private var viewHeight = 0
private var viewWidth = 0
private var waveCount = 0
/**
* 波峰
*/
private var waveCrest = 30
init {
ringPaint.color = Color.WHITE
ringPaint.alpha = 50
ringPaint.style = Paint.Style.STROKE
ringPaint.isAntiAlias = true
circlePaint.color = Color.WHITE
circlePaint.style = Paint.Style.STROKE
ringPaint.isAntiAlias = true
wavePaint.strokeWidth = ConvertUtils.dp2px(1f).toFloat()
wavePaint.color = Color.WHITE
wavePaint.style = Paint.Style.FILL
wavePaint.alpha = 50
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//var width = min(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.getSize(heightMeasureSpec))
val width = min(measure(widthMeasureSpec,true),measure(heightMeasureSpec,false))
ringPaint.strokeWidth = (width / 20).toFloat()
circlePaint.strokeWidth = ringPaint.strokeWidth / 8
ringRectF = RectF(0f+ringPaint.strokeWidth/2,0f+ringPaint.strokeWidth/2,
width.toFloat()-ringPaint.strokeWidth/2,width.toFloat()-ringPaint.strokeWidth/2)
var circleRectF = RectF(0f+ringPaint.strokeWidth+circlePaint.strokeWidth/2,0f+ringPaint.strokeWidth+circlePaint.strokeWidth/2,
width.toFloat()-ringPaint.strokeWidth-circlePaint.strokeWidth,width.toFloat()-ringPaint.strokeWidth-circlePaint.strokeWidth)
circlePath.addOval(circleRectF,Path.Direction.CW)
viewHeight = width
viewWidth = width
waveLength = (width * 3f).roundToInt()
waveCrest = width / 12
waveCount = (viewWidth / waveLength + 1.5).roundToInt()
curHeight = viewHeight / 2
//super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(width,width)
}
private fun measure( measureSpec :Int, isWidth : Boolean) : Int{
var result = 0
val mode = MeasureSpec.getMode(measureSpec);
val size = MeasureSpec.getSize(measureSpec);
val padding = if(isWidth) getPaddingLeft() + getPaddingRight()
else getPaddingTop() + getPaddingBottom()
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = if(isWidth) getSuggestedMinimumWidth()
else getSuggestedMinimumHeight();
result += padding;
if (mode == MeasureSpec.AT_MOST) {
if (isWidth) {
result = Math.max(result, size);
} else {
result = Math.min(result, size);
}
}
}
return result
}
override fun onDraw(canvas: Canvas?) {
canvas?.let{
it.drawOval(ringRectF,ringPaint)
it.drawPath(circlePath,circlePaint)
wavePath.reset()
wavePath.moveTo((-waveLength + offset).toFloat(), curHeight.toFloat());
for (i in 0 until waveCount) {
wavePath.quadTo(
((-waveLength * 3 / 4) + (i * waveLength) + offset).toFloat(),
(curHeight + waveCrest).toFloat(),
((-waveLength / 2) + (i * waveLength) + offset).toFloat(), curHeight.toFloat()
)
wavePath.quadTo(
((-waveLength / 4) + (i * waveLength) + offset).toFloat(),
(curHeight - waveCrest).toFloat(),
(i * waveLength + offset).toFloat(), curHeight.toFloat()
)
}
wavePath.lineTo(viewWidth.toFloat(), viewHeight.toFloat())
wavePath.lineTo(0f, viewHeight.toFloat())
wavePath.close()
wavePath.op(circlePath,Path.Op.INTERSECT)
it.drawPath(wavePath,wavePaint)
startAnimal()
}
}
fun setProgress(progress: Int){
var pro = progress
if (pro < 0) pro = 0
if (pro > 100) pro = 100
curHeight = viewHeight - viewHeight * pro / 100
}
/**
* 动画是否开始
*/
private var isStart = false
@Synchronized
private fun startAnimal(){
if(isStart) return
isStart = true
val animator = ValueAnimator.ofInt(0,waveLength)
animator.duration = 1000
animator.repeatCount = ValueAnimator.INFINITE
animator.interpolator = LinearInterpolator()
animator.addUpdateListener {
offset = it.animatedValue as Int
postInvalidate()
}
animator.start()
}
}