80行代码实现 Android 水波纹 自定义View

上图效果图

device-2023-04-27-142045

上代码:

class WaterView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val wavePaint = Paint(Paint.ANTI_ALIAS_FLAG)
//    定义一个Path对象用来绘制波浪的路径;
    private val wavePath = Path()

    private var waveHeight = 30f // 波浪高度
    private var waveWidth = screenWidth.toFloat() // 波长
    private var waveOffset = 0f // 相位

    private var halfWaveWidth:Float = 0f
    private var midY:Float = 0f

    init {
        wavePaint.color = Color.BLUE
        wavePaint.style = Paint.Style.FILL
        halfWaveWidth = waveWidth / 2
        midY = screenHeight / 2f
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
//        waveWidth = w.toFloat()
    }

    override fun onDraw(canvas: Canvas?) {
        wavePath.reset()
        //x从屏幕外,直到走到0点
        Log.d("JMGG", "onDraw: moveTo${-waveWidth + waveOffset} $midY")
        wavePath.moveTo(-waveWidth + waveOffset, midY)
//        计算出正弦函数的值作为波浪高度,并且根据周期和相位等因素来修改波浪形状
        for (i in -waveWidth.toInt() until width step waveWidth.toInt()) {
            wavePath.rQuadTo(halfWaveWidth / 2, waveHeight, halfWaveWidth, 0f)
            wavePath.rQuadTo(halfWaveWidth / 2, -waveHeight, halfWaveWidth, 0f)
        }
//        使用Path.lineTo()方法将计算得到的每个点添加到Path中,
//        然后使用Canvas.drawPath()方法将Path绘制出来;
        wavePath.lineTo(width.toFloat(), height.toFloat())
        wavePath.lineTo(0f, height.toFloat())
        wavePath.close()

        // 绘制渐变
        //在onDraw()方法中创建一个LinearGradient对象,并设置startColor、endColor和循环模式。
        val shader = LinearGradient(
            0f, height / 2f - waveHeight,
            0f, height.toFloat(),
            intArrayOf(Color.parseColor("#3300CCFF"), Color.TRANSPARENT),
            floatArrayOf(0f,1f),
            Shader.TileMode.CLAMP
        )
//        将该Gradient传入Paint对象的shader属性,从而使渐变效果应用到波浪上面。
        wavePaint.shader = shader

        // 绘制阴影
//        在Paint对象上调用setShadowLayer()方法,添加阴影的效果。
        wavePaint.setShadowLayer(dip(4).toFloat(), 0f, 0f, Color.parseColor("#44000000"))


        canvas?.drawPath(wavePath, wavePaint)
//        最后绘制波浪到画布上,然后清除shader属性和shadow layer。
        wavePaint.shader = null
        wavePaint.clearShadowLayer()
    }

//    使用ValueAnimator或者ObjectAnimator等动画库来产生动态波浪效果。
    fun startAnimation() {
        val animator = ObjectAnimator.ofFloat(this, "waveOffset", 0f, waveWidth)
        animator.duration = 1600
        animator.repeatCount = ValueAnimator.INFINITE
        animator.interpolator = LinearInterpolator()
        animator.start()
    }

    fun setWaveOffset(value: Float) {
        waveOffset = value
        invalidate()
    }


}

上思路:

先说能学到得东西:正弦函数(sin),了解贝塞尔曲线的绘制,动画的添加。

如果在activity中调用startAnimation()方法,是不会有动画的,在理清思路前建议先别加,来看看草图,正弦函数图解也就是第二、三、四、五步骤一个规律的波浪线。

这里的绘制步骤有8步,可以算成三个屏幕,然后连接成在一起

第一步:将点移动到屏幕外一点

wavePath.moveTo(-waveWidth + waveOffset, midY)

第二步:绘制贝塞尔曲线,取了屏幕的一半的一半,然后像y轴走了30f,这里为什么是正的呢?因为自定义view绘制的坐标轴为:

所以看起30f是向下的。

wavePath.rQuadTo(halfWaveWidth / 2, waveHeight, halfWaveWidth, 0f)

第三步:rQuadTo是累加的,所以这里就到了屏幕的右边的一半的一半,y轴走了-30f,和第二部相反向上面去了

wavePath.rQuadTo(halfWaveWidth / 2, -waveHeight, halfWaveWidth, 0f)

第四步:

第五步:

重复二三步骤进行累加,绘制超过屏幕外,如果不超过会很鬼畜,可以把for循环关掉试试

for (i in -waveWidth.toInt() until width step waveWidth.toInt()) {
            wavePath.rQuadTo(halfWaveWidth / 2, waveHeight, halfWaveWidth, 0f)
            wavePath.rQuadTo(halfWaveWidth / 2, -waveHeight, halfWaveWidth, 0f)
        }

第六步:

第七步:

连接底部的两条线

wavePath.lineTo(width.toFloat(), height.toFloat())
        wavePath.lineTo(0f, height.toFloat())

第八步:

连接到起点实现闭合。

wavePath.close()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值