Android 自定义球型水波纹带圆弧进度效果(实例代码)
需求
如下,实现一个圆形水波纹,带进度,两层水波纹需要渐变显示,且外围有一个圆弧进度。
思路
外围圆弧进度:可以通过canvas.drawArc()实现。由于圆弧需要实现渐变,可以通过给画笔设置shader(SweepGradient)渲染,为了保证圆弧起始的颜色值始终一致,需要动态调整shader的参数。具体参见
SweepGradient(centerX.toFloat(),centerY.toFloat(),circleColors[0],floatArrayOf(0f,value/100f))
第四个参数需要根据当前进度填写对应数据比例。不懂的同学可以自行百度查阅。
水波纹的实现:直接使用贝塞尔曲线Path.quadTo()实现,通过拉伸水平直线绘制波浪效果。可以通过控制拉伸点(waveAmplitude)距离水平线的高度,达到波浪高度的控制。至于波浪的移动,可以通过移动平移水平线的起始位置来实现,在使用动画循环即可,为了能够稳定的显示,绘制波浪时需要严格绘制整数倍周期的波浪。
园形的实现:绘制一个完整的圆形,然后通过Path.op()合并裁剪水波纹path。注意点就是Android6有个坑,使用该方法会有明显的抖动,为了解决该问题,我的做法是多画一层圆弧以掩盖此抖动。
生命周期的控制:为了减少某些时刻CPU的损耗,通过控制变量自定义lifeDelegate(基于kotlin的代理模式实现)来控制动画的开始暂停。由于笔者使用的框架基于MVVM,所以代码就没有使用attrs控制属性,这里就不做过多的修改了。
整体实现
classWaveView(context:Context,attributeSet:AttributeSet?=null):View(context,attributeSet){
companionobject{
constvalRESUME=0x1
constvalSTOP=0x2
constvalDESTROY=0x3
}
privatevarmWidth=0//控件整体宽度
privatevarmHeight=0//控件整体高度
//控件中心位置,x,y坐标
privatevarcenterX=0
privatevarcenterY=0
privatevarouterRadius=0//外圈圆环的半径
privatevarinnerRadius=250f//内部圆圈的半径
privatevarradiusDist=50f//内外圆圈的半径差距
privatevarfWaveShader:LinearGradient?=null
privatevarsWaveShader:LinearGradient?=null
privatevarwavePath=Path()
privatevarwaveCirclePath=Path()
privatevalwaveNum=2
//波浪的渐变颜色数组
privatevalwaveColorsbylazy{
arrayListOf(
//深红色
intArrayOf(Color.parseColor("#E8E6421A"),Color.parseColor("#E2E96827")),
intArrayOf(Color.parseColor("#E8E6421A"),Color.parseColor("#E2F19A7F")),
//橙色
intArrayOf(Color.parseColor("#E8FDA085"),Color.parseColor("#E2F6D365")),
intArrayOf(Color.parseColor("#E8FDA085"),Color.parseColor("#E2F5E198")),
//绿色
intArrayOf(Color.parseColor("#E8009EFD"),Color.parseColor("#E22AF598")),
intArrayOf(Color.parseColor("#E8009EFD"),Color.parseColor("#E28EF0C6"))
)
}
//外围圆环的渐变色
privatevalcircleColorsbylazy{
arrayListOf(
//深红色
intArrayOf(Color.parseColor("#FFF83600"),Color.parseColor("#FFF9D423")),
//橙色
intArrayOf(Color.parseColor("#FFFDA085"),Color.parseColor("#FFF6D365")),
//绿色
intArrayOf(Color.parseColor("#FF2AF598"),Color.parseColor("#FF009EFD"))
)
}
privatevalwavePaintbylazy{
valpaint=Paint()
paint.isAntiAlias=true
paint.strokeWidth=1f
paint
}
//波浪高度比例
privatevarwaveWaterLevelRatio=0f
//波浪的振幅
privatevarwaveAmplitude=0f
//波浪最大振幅高度
privatevarmaxWaveAmplitude=0f
//外围圆圈的画笔
privatevalouterCirclePaintbylazy{
valpaint=Paint()
paint.strokeWidth=20f
paint.strokeCap=Paint.Cap.ROUND
paint.style=Paint.Style.STROKE
paint.isAntiAlias=true
paint
}
privatevalouterNormalCirclePaintbylazy{
valpaint=Paint()
paint.strokeWidth=20f
paint.color=Color.parseColor("#FFF2F3F3")
paint.style=Paint.Style.STROKE
paint.isAntiAlias=true
paint
}
privatevalbgCirclePaintbylazy{
valpaint=Paint()
paint.color=Color.parseColor("#FFF6FAFF")
paint.style=Paint.Style.FILL
paint.isAntiAlias=true
paint
}
privatevaltextPaintbylazy{
valpaint=Paint()
paint.style=Paint.Style.FILL
paint.textAlign=Paint.Align.CENTER
paint.isFakeBoldText=true
paint.isAntiAlias=true
paint
}
privatevalringPaintbylazy{
valpaint=Paint()
paint.style=Paint.Style.STROKE
paint.color=Color.WHITE
paint.isAntiAlias=true
paint
}
//外围圆圈所在的矩形
privatevalouterCircleRectfbylazy{
valrectF=RectF()
rectF.set(
centerX-outerRadius+outerCirclePaint.strokeWidth,
centerY-outerRadius+outerCirclePaint.strokeWidth,
centerX+outerRadius-outerCirclePaint.strokeWidth,
centerY+outerRadius-outerCirclePaint.strokeWidth
)
rectF
}
//外围圆圈的颜色渐变器矩阵,用于从90度开启渐变,由于线条头部有个小圆圈会导致显示差异,因此从88度开始绘制
privatevalsweepMatrixbylazy{
valmatrix=Matrix()
matrix.setRotate(88f,centerX.toFloat(),centerY.toFloat())
matrix
}
//进度0-100
varpercent=0
set(value){
field=value
waveWaterLevelRatio=value/100f
//y=-4*x2+4x抛物线计算振幅,水波纹振幅规律更加真实
waveAmplitude=
(-4*(waveWaterLevelRatio*waveWaterLevelRatio)+4*waveWaterLevelRatio)*maxWaveAmplitude
//waveAmplitude=if(value<50)2f*waveWaterLevelRatio*maxWaveAmplitudeelse(-2*waveWaterLevelRatio+2)*maxWaveAmplitude
valshader=when(value){
in0..46->{
fWaveShader=LinearGradient(
0f,mHeight.toFloat(),0f,mHeight*(1-waveWaterLevelRatio),
waveColors[0],
null,Shader.TileMode.CLAMP
)
sWaveShader=LinearGradient(
0f,mHeight.toFloat(),0f,mHeight*(1-waveWaterLevelRatio),
waveColors[1],
null,Shader.TileMode.CLAMP
)
SweepGradient(
centerX.toFloat(),
centerY.toFloat(),
circleColors[0],
floatArrayOf(0f,value/100f)
)
}
in47..54->{
fWaveShader=LinearGradient(
0f,mHeight.toFloat(),0f,mHeight*(1-waveWaterLevelRatio),
waveColors[2],
null,Shader.TileMode.CLAMP
)
sWaveShader=LinearGradient(
0f,mHeight.toFloat(),0f,mHeight*(1-waveWaterLevelRatio),
waveColors[3],
null,Shader.TileMode.CLAMP
)
SweepGradient(
centerX.toFloat(),
centerY.toFloat(),
circleColors[1],
floatArrayOf(0f,value/100f)
)
}
else->{
fWaveShader=LinearGradient(
0f,mHeight.toFloat(),0f,mHeight*(1-waveWaterLevelRatio),
waveColors[4],
null,Shader.TileMode.CLAMP
)
sWaveShader=LinearGradient(
0f,mHeight.toFloat(),0f,mHeight*(1-waveWaterLevelRatio),
waveColors[5],
null,Shader.TileMode.CLAMP
)
SweepGradient(
centerX.toFloat(),
centerY.toFloat(),
circleColors[2],
floatArrayOf(0f,value/100f)
)
}
}
shader.setLocalMatrix(sweepMatrix)
outerCirclePaint.shader=shader
invalidate()
}
privatevalgreedTip="GreedIndex"
//文本的字体大小
privatevarpercentSize=80f
privatevargreedSize=30f
privatevartextColor=Color.BLACK
//外围圆圈的画笔大小
privatevarouterStrokeWidth=10f
privatevarfAnimatedValue=0f
privatevarsAnimatedValue=0f
//动画
privatevalfValueAnimatorbylazy{
valvalueAnimator=ValueAnimator()
valueAnimator.duration=1500
valueAnimator.repeatCount=ValueAnimator.INFINITE
valueAnimator.interpolator=LinearInterpolator()
valueAnimator.setFloatValues(0f,waveWidth)
valueAnimator.addUpdateListener{animation->
fAnimatedValue=animation.animatedValueasFloat
invalidate()
}
valueAnimator
}
privatevalsValueAnimatorbylazy{
valvalueAnimator=ValueAnimator()
valueAnimator.duration=2000
valueAnimator.repeatCount=ValueAnimator.INFINITE
valueAnimator.interpolator=LinearInterpolator()
valueAnimator.setFloatValues(0f,waveWidth)
valueAnimator.addUpdateListener{animation->
sAnimatedValue=animation.animatedValueasFloat
invalidate()
}
valueAnimator
}
//一小段完整波浪的宽度
privatevarwaveWidth=0f
varlifeDelegatebyDelegates.observable(0){_,old,new->
when(new){
RESUME->onResume()
STOP->onPause()
DESTROY->onDestroy()
}
}
//设置中间进度文本的字体大小
funsetPercentSize(size:Float){
percentSize=size
invalidate()
}
//设置中间提示文本的字体大小
funsetGreedSize(size:Float){
greedSize=size
invalidate()
}
//设置文本颜色
funsetTextColor(color:Int){
textColor=color
textPaint.color=textColor
invalidate()
}
//设置外围圆圈的宽度
funsetOuterStrokeWidth(width:Float){
outerStrokeWidth=width
outerCirclePaint.strokeWidth=outerStrokeWidth
outerNormalCirclePaint.strokeWidth=outerStrokeWidth
invalidate()
}
//设置内圆半径
funsetInnerRadius(radius:Float){
innerRadius=radius
invalidate()
}
overridefunonSizeChanged(w:Int,h:Int,oldw:Int,oldh:Int){
super.onSizeChanged(w,h,oldw,oldh)
mWidth=width-paddingStart-paddingEnd
mHeight=height-paddingTop-paddingBottom
centerX=mWidth/2
centerY=mHeight/2
outerRadius=mWidth.coerceAtMost(mHeight)/2
radiusDist=outerRadius-innerRadius
waveWidth=mWidth*1.8f
maxWaveAmplitude=mHeight*0.15f
}
privatefunonResume(){
if(fValueAnimator.isStarted){
animatorResume()
}else{
fValueAnimator.start()
sValueAnimator.start()
}
}
privatefunanimatorResume(){
if(fValueAnimator.isPaused||!fValueAnimator.isRunning){
fValueAnimator.resume()
}
if(sValueAnimator.isPaused||!sValueAnimator.isRunning){
sValueAnimator.resume()
}
}
privatefunonPause(){
if(fValueAnimator.isRunning){
fValueAnimator.pause()
}
if(sValueAnimator.isRunning){
sValueAnimator.pause()
}
}
privatefunonDestroy(){
fValueAnimator.cancel()
sValueAnimator.cancel()
}
//当前窗口销毁时,回收动画资源
overridefunonDetachedFromWindow(){
onDestroy()
super.onDetachedFromWindow()
}
overridefunonDraw(canvas:Canvas){
drawCircle(canvas)
drawWave(canvas)
drawText(canvas)
}
privatefundrawWave(canvas:Canvas){
//波浪当前高度
vallevel=(1-waveWaterLevelRatio)*innerRadius*2+radiusDist
//绘制所有波浪
for(numin0untilwaveNum){
//重置path
wavePath.reset()
waveCirclePath.reset()
varstartX=if(num==0){//第一条波浪的起始位置
wavePath.moveTo(-waveWidth+fAnimatedValue,level)
-waveWidth+fAnimatedValue
}else{//第二条波浪的起始位置
wavePath.moveTo(-waveWidth+sAnimatedValue,level)
-waveWidth+sAnimatedValue
}
while(startX
总结
以上所述是小编给大家介绍的Android自定义球型水波纹带圆弧进度效果(实例代码),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。