Android 水波纹扩散效果实现

人生只是一种体验,不必用来演绎完美。

效果图

View源码

java 复制代码
package com.android.circlescalebar.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
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 android.view.animation.LinearInterpolator;

import androidx.annotation.NonNull;

import com.android.circlescalebar.utils.DensityUtils;
import java.util.ArrayList;
import java.util.List;
public final class WaveView extends View {

    private static final String TAG = "WaveView";

    private int centerColor  = Color.GREEN;
    private int centerRadius = DensityUtils.dp2px(1.0F);
    private int maxRadius = DensityUtils.dp2px(105.0F);
    private int waveIntervalTime= 500;
    private int waveDuration = 1500;
    private boolean running;
    private List<Wave> waveList = new ArrayList();;
    private int waveWidth = DensityUtils.dp2px(1.0F);
    private Paint paint = new Paint();

    public WaveView(Context context) {
        super(context);
    }

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

    public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setWaveStart(boolean waveStart) {
        if (waveStart) {
            if (!running) {
                running = true;
                waveList.add(new Wave());
            }
        } else {
            running = false;
            for (Wave wave : waveList) {
                wave.cancelAnimation();
            }
        }
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        int radius = (int)((float)Math.min(w, h) / 2.0F);
        if (radius <maxRadius) {
            maxRadius = radius;
        }
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(centerColor);
        for (Wave wave : waveList) {
            paint.setAlpha(wave.getAlpha());
            paint.setStrokeWidth((float)this.waveWidth);
            paint.setStyle(Paint.Style.STROKE);
            canvas.drawCircle((float)(this.getWidth() / 2), (float)(this.getHeight() / 2), wave.getCurrentRadius(),paint);
        }

        if (this.waveList.size() > 0) {
            paint.setAlpha(255);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawCircle((float)(this.getWidth() / 2), (float)(this.getHeight() / 2), (float)this.centerRadius,paint);
        }
    }

    public final class Wave {
        public boolean hasCreateNewWave;
        public final ValueAnimator createWaveAnimation;
        public float percent;

//        public final float getPercent() {
//            return percent;
//        }
//
//        public final void setPercent(float value) {
//
//            Log.d(TAG, "Wave: percent" + value);
//            percent = value;
//            if (running && value >= (float)waveIntervalTime / (float)waveDuration && !this.hasCreateNewWave) {
//                waveList.add(new Wave());
//                hasCreateNewWave = true;
//            }
//            invalidate();
//        }

        public void cancelAnimation() {
            createWaveAnimation.cancel();
        }

        public int getAlpha() {
            return (int)((float)255 * ((float)1 - percent));
        }

        public float getCurrentRadius() {
            return (float)centerRadius +percent * (float)(maxRadius - centerRadius);
        }

        public Wave() {
//            createWaveAnimation = ObjectAnimator.ofFloat(this, "percent", 0F, 1F);
            createWaveAnimation = ObjectAnimator.ofFloat(this, "alpha", 0F, 1F);
            createWaveAnimation.setInterpolator(new LinearInterpolator());
            createWaveAnimation.setDuration(waveDuration);
            createWaveAnimation.start();

            createWaveAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(@NonNull ValueAnimator valueAnimator) {
                    float animatedValue = (float) valueAnimator.getAnimatedValue();
                    percent = animatedValue;
                    if (running && animatedValue >= (float)waveIntervalTime / (float)waveDuration && !hasCreateNewWave) {
                        waveList.add(new Wave());
                        hasCreateNewWave = true;
                    }
                    invalidate();
                }
            });

            createWaveAnimation.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    waveList.remove(this);
                }
            });
        }
    }
}

工具类

java 复制代码
package com.android.circlescalebar.utils;

import android.content.res.Resources;

public class DensityUtils {
    public float density;

    public DensityUtils() {
        density = Resources.getSystem().getDisplayMetrics().density;
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     * @param dpValue 虚拟像素
     * @return 像素
     */
    public static int dp2px(float dpValue) {
        return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     * @param pxValue 像素
     * @return 虚拟像素
     */
    public static float px2dp(int pxValue) {
        return (pxValue / Resources.getSystem().getDisplayMetrics().density);
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     * @param dpValue 虚拟像素
     * @return 像素
     */
    public int dip2px(float dpValue) {
        return (int) (0.5f + dpValue * density);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     * @param pxValue 像素
     * @return 虚拟像素
     */
    public float px2dip(int pxValue) {
        return (pxValue / density);
    }
}

调用实现

java 复制代码
 waveView.setWaveStart(true); 

布局

java 复制代码
<com.android.circlescalebar.view.WaveView
    android:id="@+id/waveView"
    android:layout_width="215dp"
    android:layout_height="215dp"
    android:layout_gravity="center"/>   
相关推荐
拭心1 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王3 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡3 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道4 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库5 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道5 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe5 小时前
Android Hook - 动态加载so库
android
居居飒6 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He9 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗9 小时前
Android笔试面试题AI答之Android基础(1)
android