Android 实现水波纹动效

本文详细介绍了一种在Android中实现水波纹动画效果的方法,通过自定义View和Canvas绘图,展示了如何调整波纹的颜色、半径和移动速度等参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android 实现水波纹动效

WaterRippleView核心文件

package com.manss.myapplication.widget.animation

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import com.manss.myapplication.widget.DisplayUtil
import java.util.*

/**
 * 水波纹动画view
 */
class WaterRippleView : View {
    private var mMaxWaveAreaRadius = 0f
    private var mWaveIntervalSize = 0f//波距 = 0f
    private var mStirStep = 0f // 波移动的步幅 = 0f
    private var mWidth = 0
    private var mWaveStartWidth = 0f// px = 0f
    private var mWaveEndWidth = 0f// px 最大半径,超过波消失 = 0f
    private var mWaveColor = 0
    private var mViewCenterX = 0f
    private var mViewCenterY = 0f
    private val rippleColor = Color.BLUE
    //波动属性设置
    private val mWavePaint = Paint()
    //中心点属性设置
    private val mWaveCenterShapePaint = Paint()
    private var mFillAllView = false
    private var mFillWaveSourceShapeRadius = 0f
    private val mWaves: MutableList<Wave?> = ArrayList()

    constructor(context: Context?, attrs: AttributeSet?) : super(
        context,
        attrs
    ) {
        init()
    }

    constructor(context: Context?) : super(context) {
        init()
    }

    private fun init() {
        setWaveInfo(2f, 1f, 2f, 15f, rippleColor)
        mWaveIntervalSize = DisplayUtil.dip2px(context, 20f).toFloat()
        mWidth = DisplayUtil.dip2px(context, 2f)
        //初始化波动最大半径
        mWaveEndWidth = DisplayUtil.dip2px(context, 100f).toFloat()
    }

    override fun onLayout(
        changed: Boolean, left: Int, top: Int, right: Int,
        bottom: Int
    ) {
        super.onLayout(changed, left, top, right, bottom)
        mViewCenterX = width / 2.toFloat()
        mViewCenterY = height / 2.toFloat()
        var waveAreaRadius = mMaxWaveAreaRadius
        waveAreaRadius = if (mFillAllView) {
            Math.sqrt(
                (mViewCenterX * mViewCenterX
                        + mViewCenterY * mViewCenterY).toDouble()
            ).toFloat()
        } else {
            Math.min(mViewCenterX, mViewCenterY)
        }
        if (mMaxWaveAreaRadius != waveAreaRadius) {
            mMaxWaveAreaRadius = waveAreaRadius
            resetWave()
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        stir()
        for (w in mWaves) {
            mWavePaint.color = w!!.color
            mWavePaint.strokeWidth = mWidth.toFloat()
            mWavePaint.alpha = w.alpha
            canvas.drawCircle(mViewCenterX, mViewCenterY, w.radius, mWavePaint)
        }
        if (mFillWaveSourceShapeRadius > 0f) {
            canvas.drawCircle(
                mViewCenterX, mViewCenterY,
                mFillWaveSourceShapeRadius, mWaveCenterShapePaint
            )
        }
        postInvalidateDelayed(FPS.toLong())
    }

    /**
     * 波
     *
     */
    internal inner class Wave {
        var radius = 0f
        var width = 0f
        var color = 0
        var alpha = 0
        fun reset() {
            radius = 0f
            width = mWaveStartWidth
            color = mWaveColor
        }

        override fun toString(): String {
            return ("Wave [radius=" + radius + ", width=" + width + ", color="
                    + color + "]")
        }

        init {
            reset()
        }
    }

    private var mLastRmoveWave: Wave? = null
    /**
     * 触发涌动传播
     */
    private fun stir() {
        val nearestWave = if (mWaves.isEmpty()) null else mWaves[0]
        if (nearestWave == null || nearestWave.radius >= mWaveIntervalSize) {
            var w: Wave? = null
            if (mLastRmoveWave != null) {
                w = mLastRmoveWave
                mLastRmoveWave = null
                w!!.reset()
            } else {
                w = Wave()
            }
            mWaves.add(0, w)
        }
        val waveWidthIncrease = mWaveEndWidth - mWaveStartWidth
        val size = mWaves.size
        for (i in 0 until size) {
            val w = mWaves[i]
            var rP = w!!.radius / mMaxWaveAreaRadius
            if (rP > 1f) {
                rP = 1f
            }
            w.width = mWaveStartWidth + rP * waveWidthIncrease
            w.radius += mStirStep
            w.color = rippleColor
            w.alpha = 255 / (i + 1)
        }
        val farthestWave = mWaves[size - 1]
        if (farthestWave!!.radius > mWaveEndWidth) {
            mWaves.removeAt(size - 1)
        }
    }

    /**
     * 如果true会选择view的最大的对角线作为活动半径
     *
    */
    fun setFillAllView(fillAllView: Boolean) {
        mFillAllView = fillAllView
        resetWave()
    }

    fun resetWave() {
        mWaves.clear()
        postInvalidate()
    }

    /**
     * 填充波形起源的中心点
     *
     * @param radius 半径大小
     */
    fun setFillWaveSourceShapeRadius(radius: Float) {
        mFillWaveSourceShapeRadius = radius
    }

    /**
     * 设置波形属性
     *
     * @param intervalSize 两个波形之间的间距
     * @param stireStep    波形移动速度
     * @param startWidth   起始波形宽度
     * @param endWidth     终止波形宽度
     * @param color        波形颜色
     */
    fun setWaveInfo(
        intervalSize: Float, stireStep: Float,
        startWidth: Float, endWidth: Float, color: Int
    ) {
        mWaveIntervalSize = intervalSize
        mStirStep = stireStep
        mWaveStartWidth = startWidth
        mWaveEndWidth = endWidth
        setWaveColor(color)
        resetWave()
    }

    /**
     * 设置波形颜色
     *
     * @param color
     */
    fun setWaveColor(color: Int) {
        mWaveColor = color
        mWaveCenterShapePaint.color = mWaveColor
    }

    companion object {
        private const val FPS = 1000 / 40
    }

    init {
        mWavePaint.isAntiAlias = true
        mWavePaint.style = Paint.Style.FILL
    }

    init {
        mWaveCenterShapePaint.isAntiAlias = true
        mWaveCenterShapePaint.style = Paint.Style.FILL
    }
}

 

util文件

package com.manss.myapplication.widget;

import android.content.Context;

public class DisplayUtil {
    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }
}

activity_water_ripple_view.xml文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white">

        <com.manss.myapplication.widget.animation.WaterRippleView
            android:id="@+id/activity_water_ripple_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

方法调用

 

package com.example.myapplication

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.manss.myapplication.R
import com.manss.myapplication.widget.DisplayUtil
import com.manss.myapplication.widget.animation.WaterRippleView

/**
 * 水波纹动画
 */
class WaterRippleActivity :AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_water)

        var waterRipple: WaterRippleView = findViewById(R.id.activity_water_ripple_view)
        waterRipple.setFillWaveSourceShapeRadius(DisplayUtil.dip2px(this,15f).toFloat())
//        waterRipple.setFillAllView(true)
//        waterRipple.setWaveColor(android.R.color.holo_blue_bright)
    }
}

总结,如果波纹是线条效果,修改WaterRippleView文件的

    init {
        mWavePaint.isAntiAlias = true
        mWavePaint.style = Paint.Style.FILL
    }

修改后

    init {
        mWavePaint.isAntiAlias = true
        mWavePaint.style = Paint.Style.STROKE
    }

修改波纹半径

    //初始化波动最大半径
        mWaveEndWidth = DisplayUtil.dip2px(context, 100f).toFloat()

修改波纹颜色

  private val rippleColor = Color.BLUE
    //波动属性设置

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值