如何使用Android Canvas绘制这种波浪圆形?
Android Canvas 实现圆形波浪效果
核心思路
不用硬调贝塞尔曲线控制点,直接用正弦函数计算波浪顶点坐标,结合Canvas的裁剪功能把绘制区域限制在圆形内,能得到更自然的波浪形态。
具体实现代码
自定义View类
class WaveCircleView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.parseColor("#4CAF50") style = Paint.Style.FILL } private val path = Path() private var waveOffset = 0f private val waveHeight = 20f // 波浪高度 private val waveWidth = 100f // 波浪波长 private var progress = 0.5f // 进度范围0-1 // 波浪滚动动画 private val valueAnimator = ValueAnimator.ofFloat(0f, waveWidth).apply { duration = 1500 repeatCount = ValueAnimator.INFINITE interpolator = LinearInterpolator() addUpdateListener { waveOffset = it.animatedValue as Float invalidate() } } init { valueAnimator.start() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) val centerX = width / 2f val centerY = height / 2f val radius = min(width, height) / 2f // 裁剪出圆形绘制区域 canvas.save() val clipPath = Path() clipPath.addCircle(centerX, centerY, radius, Path.Direction.CW) canvas.clipPath(clipPath) // 根据进度计算波浪基线位置 val baseY = centerY + radius * (1 - progress * 2) // 构建波浪路径 path.reset() path.moveTo(-waveWidth + waveOffset, baseY) var x = -waveWidth + waveOffset while (x <= width + waveWidth) { // 正弦函数计算波浪顶点Y坐标 val y = baseY + Math.sin((x / waveWidth) * 2 * Math.PI) * waveHeight path.lineTo(x, y.toFloat()) x += 20f // 步长越小波浪越平滑 } // 闭合路径,填充到圆形底部 path.lineTo(width.toFloat(), height.toFloat()) path.lineTo(0f, height.toFloat()) path.close() canvas.drawPath(path, paint) canvas.restore() // 绘制圆形边框(可选) val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.parseColor("#333333") style = Paint.Style.STROKE strokeWidth = 4f } canvas.drawCircle(centerX, centerY, radius, borderPaint) } // 对外设置进度的方法 fun setProgress(progress: Float) { this.progress = progress.coerceIn(0f, 1f) invalidate() } }
布局与代码调用
- 布局中添加自定义View:
<com.yourpackage.WaveCircleView android:id="@+id/waveCircle" android:layout_width="200dp" android:layout_height="200dp" />
- 代码中设置进度:
val waveCircle = findViewById<WaveCircleView>(R.id.waveCircle) waveCircle.setProgress(0.7f) // 设置70%填充进度
关键细节说明
- 正弦曲线生成波浪:相比贝塞尔曲线,正弦函数能生成均匀自然的波浪,避免手动调整控制点的繁琐。
- 圆形裁剪:通过
clipPath限制绘制区域,不用处理波浪超出圆形的部分。 - 滚动动画:偏移正弦曲线X轴实现波浪流动效果,动画流畅无卡顿。
- 进度控制:通过调整波浪基线位置,轻松实现不同填充比例的效果。
之前用贝塞尔曲线效果不佳,大多是因为控制点位置计算不精准,用正弦曲线的方式能从根源解决这个问题,匹配示例图的预期效果。
内容的提问来源于stack exchange,提问作者mkki
相关产品推荐
相关产品推荐

