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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。