You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何实现自定义形状的WaterWave百分比填充效果

实现自定义形状的水波填充效果

核心思路

通过自定义View,结合Path裁剪和正弦曲线绘制水波,根据百分比控制水波的垂直偏移量,即可实现任意自定义形状的水波填充效果。

具体实现步骤

1. 搭建自定义View基础结构

创建继承自View的类,初始化画笔、路径等核心变量,并定义自定义形状的Path:

class CustomShapeWaveView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    // 自定义形状的路径(替换成你的目标形状)
    private val shapePath = Path()
    // 水波填充画笔
    private val wavePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#4A90E2")
        style = Paint.Style.FILL
    }
    // 形状轮廓画笔(可选,用于显示形状边框)
    private val shapeStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#333333")
        style = Paint.Style.STROKE
        strokeWidth = 2f
    }
    // 水波水平偏移量,用于实现滚动动画
    private var waveOffset = 0f
    // 当前填充百分比(0-100)
    private var fillPercent = 0f

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 重置自定义形状路径,这里替换成你的目标形状逻辑
        resetTargetShapePath(w, h)
    }

    private fun resetTargetShapePath(width: Int, height: Int) {
        shapePath.reset()
        // 示例:绘制一个自定义不规则形状,你可以替换成自己的Path逻辑
        val centerX = width / 2f
        val centerY = height / 2f
        shapePath.moveTo(centerX, centerY - height/3)
        shapePath.lineTo(centerX + width/3, centerY + height/4)
        shapePath.lineTo(centerX + width/5, centerY + height/2)
        shapePath.lineTo(centerX - width/5, centerY + height/2)
        shapePath.lineTo(centerX - width/3, centerY + height/4)
        shapePath.close()
    }
}

2. 结合形状裁剪与水波绘制

通过画布裁剪限制水波绘制区域,再根据百分比计算水波的基准位置,绘制动态水波:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // 绘制形状轮廓(可选)
    canvas.drawPath(shapePath, shapeStrokePaint)

    // 裁剪画布,仅在自定义形状区域内绘制内容
    canvas.save()
    canvas.clipPath(shapePath)

    // 计算水波基准Y轴位置:0%在View底部,100%在View顶部
    val baseWaveY = height - (fillPercent / 100f) * height
    // 绘制水波
    drawDynamicWave(canvas, baseWaveY)

    canvas.restore()

    // 驱动水波滚动动画
    waveOffset += 4f
    postInvalidateDelayed(30)
}

private fun drawDynamicWave(canvas: Canvas, baseY: Float) {
    val wavePath = Path()
    val waveWidth = width.toFloat()
    val waveHeight = 18f // 水波起伏高度,可按需调整

    // 从View左侧外开始绘制正弦曲线
    wavePath.moveTo(-waveWidth + waveOffset, baseY)
    for (x in -waveWidth.toInt()..width step (waveWidth / 2).toInt()) {
        val currentX = x + waveOffset
        // 绘制上半段曲线
        wavePath.quadTo(
            currentX + waveWidth / 4, baseY - waveHeight,
            currentX + waveWidth / 2, baseY
        )
        // 绘制下半段曲线
        wavePath.quadTo(
            currentX + waveWidth * 3 / 4, baseY + waveHeight,
            currentX + waveWidth, baseY
        )
    }
    // 闭合路径,将水波与View底部连接形成填充区域
    wavePath.lineTo(width.toFloat(), height.toFloat())
    wavePath.lineTo(0f, height.toFloat())
    wavePath.close()

    canvas.drawPath(wavePath, wavePaint)
}

3. 添加百分比控制接口

对外暴露方法,用于动态设置填充百分比:

fun setFillPercent(percent: Float) {
    // 限制百分比在0-100范围内
    this.fillPercent = percent.coerceIn(0f, 100f)
    invalidate()
}

关键细节说明

  • 自定义形状替换:在resetTargetShapePath()方法中,将示例的不规则形状Path替换成你的目标形状(可从SVG解析Path,或手动绘制路径)。
  • 水波参数调整:修改waveHeight调整水波起伏程度,修改waveOffset的增量调整滚动速度;若不需要动画,可移除postInvalidateDelayed(),仅在百分比变化时刷新视图。
  • 性能优化:如果裁剪性能不佳,可改用PorterDuffXfermode,将水波Path与形状Path通过SRC_IN模式混合,实现相同的遮罩效果。

内容的提问来源于stack exchange,提问作者Dr.KeyOk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 18:55:34