Android 水波纹自定义 View

这篇博客介绍了如何在Android中实现水波纹扩散效果的自定义View。通过将最大圆与最小圆的半径间隔分成等份,并逐渐改变同心圆的半径并调整Paint的透明度,创建出向外散开的动画效果。示例应用于网易云音乐歌曲识别和附近搜索场景。项目源码可在GitHub上找到。

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

介绍:水波纹散开效果的控件在 App 里面还是比较常见的,例如 网易云音乐歌曲识别,附近搜索场景。

看下实现的效果:

这里写图片描述


实现思路: 先将最大圆半径与最小圆半径间距分成几等份,从内到外,Paint 透明度依次递减,绘制出同心圆,然后不断的改变这些同心圆的半径大小,延迟一定时间重绘,便达到了想外散开的动画效果了。

public class WaveView extends View {

    private static final String TAG = "WaveView";

    private int waveColor;

    private int waveCount;

    private Bitmap waveCenterIcon;

    private Paint paint;

    private int mWidth;

    private int mHeight;

    private int centerX;

    private int centerY;

    private float radius;    // 最外圆半径,即最大半径

    private float innerRadius;  // 最内圆的半径,即最小半径

    private int centerIconWidth;

    private int centerIconHeight;

    private float[] waveDegreeArr;

    private boolean isRunning = true;

    public WaveView(Context context) {
        this(context, null);
    }

    public WaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        readAttrs(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(waveColor);
        paint.setStyle(Paint.Style.FILL);
        waveDegreeArr = new float[waveCount];

        // 设置中间 drawable 点击事件


    }

    private void readAttrs(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
        try {
            waveColor = typedArray.getColor(R.styleable.WaveView_waveColor, 0xffff0000);
            waveCount = typedArray.getInt(R.styleable.WaveView_waveCount, 4);
            Drawable centerDrawable = typedArray.getDrawable(R.styleable.WaveView_waveCenterIcon);
            waveCenterIcon = ((BitmapDrawable) centerDrawable).getBitmap();
        } catch (Exception e) {

        } finally {
            typedArray.recycle();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        centerX = mWidth / 2;
        centerY = mHeight / 2;
        radius = Math.min(mWidth, mHeight) / 2f;
        centerIconWidth = waveCenterIcon.getWidth();
        centerIconHeight = waveCenterIcon.getHeight();
        innerRadius = Math.max(centerIconWidth, centerIconHeight) * 1.2f;

        for (int i = 0; i < waveCount; i++) {
            waveDegreeArr[i] = innerRadius + (radius - innerRadius) / waveCount * i;
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST) {
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(dp2Px(120), MeasureSpec.EXACTLY);
        }
        if (heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(dp2Px(120), MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        drawWave(canvas);
        drawCenterCircle(canvas);
        drawCenterIcon(canvas);

    }

    private void drawCenterCircle(Canvas canvas) {
        canvas.drawCircle(centerX, centerY, innerRadius, paint);
    }

    private void drawWave(Canvas canvas) {
        for (int i = 0; i < waveCount; i++) {
            paint.setAlpha((int) (255 - 255 * waveDegreeArr[i] / radius));
            canvas.drawCircle(centerX, centerY, waveDegreeArr[i], paint);
        }
        for (int i = 0; i < waveDegreeArr.length; i++) {
            if ((waveDegreeArr[i] += 4) > radius) {
                waveDegreeArr[i] = innerRadius;
            }
        }
        if (isRunning) {
            postInvalidateDelayed(50);
        }
    }

    private void drawCenterIcon(Canvas canvas) {
        paint.setAlpha(255);
        int left = centerX - centerIconWidth / 2;
        int top = centerY - centerIconHeight / 2;
        canvas.drawBitmap(waveCenterIcon, left, top, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                // 处理事件逻辑
                handleEvent(event);
                return true;
        }
        return true;
    }

    private void handleEvent(MotionEvent event) {
        float touchX = event.getX();
        float touchY = event.getY();
        Log.i(TAG, "handleEvent: " + "(" + touchX + "," + touchY + ")");
        float distanceX = Math.abs(touchX - centerX);
        float distanceY = Math.abs(touchY - centerY);
        // 计算触摸点距离中心点的距离
        float distance = (float) Math.sqrt(distanceX * distanceX + distanceY * distanceY);
        // 当点击的点距离中心点距离小于最内圆半径时,认为是点击有效,否则无效
        if (distance < innerRadius) {
            if (listener != null) {
                listener.onCenterWaveClick();
            }
        }
    }

    OnCenterWaveClickListener listener;

    public interface OnCenterWaveClickListener {
        void onCenterWaveClick();
    }

    public void setOnCenterWaveClickListener(OnCenterWaveClickListener listener) {
        this.listener = listener;
    }

    public void toggle() {
        isRunning = !isRunning;
        invalidate();
    }

    public boolean isWaveRunning() {
        return isRunning;
    }


    private int dp2Px(int dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
    }

}

github地址:https://github.com/xing16/WaveView

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值