Android 仿苹果siri语音动效

本文介绍了一个自定义View组件WaveView的实现细节,该组件能够模拟波浪效果,并通过动画进行展示。文章深入探讨了如何使用Android动画框架创建平滑过渡的波浪动画,包括关键属性的设置与调整。

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

 

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class WaveView extends View {
    private static final String TAG = WaveView.class.getSimpleName();
    private int mWidth, mHeight;
    private Paint mPaint;
    private int MAX;
    private float amplitude = 0.3f;
    private float wAmplitude = 1f;

    private int[] COLORS = {R.color.green, R.color.blue, R.color.pink};
    private Random random;

    private static final float MAX_VOLUME = 30;
    private List<Wave> waves;

    private int[] lineColors = new int[]{0xFF111111, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF111111};
    private float[] linepositions = new float[]{0f, 0.1f, 0.9f, 1};
    private boolean running;
    private long l;

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

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHeight = h;
        mWidth = w;

        MAX = h * 2 / 3;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mHeight = getMeasuredHeight();
        mWidth = getMeasuredWidth();
    }

    private Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:

                    running = true;
                    waveCount = 10;
                    createWave();
                    setWaveCount(waveCount);
                    postInvalidate();
                    break;
            }
        }
    };

    public void startAnim() {
        if (!running)
            handler.sendEmptyMessageDelayed(1, 100);
    }

    private int waveCount = 10;

    private void setWaveCount(int count) {
        int size = waves.size();
        if (count > size) {
            count = size;
        }
        for (int i = 0; i < size; i++) {
            if (i < count) {
                waves.get(i).playing = true;
            } else {
                waves.get(i).playing = false;
            }
        }
    }

    public void setVolume(float volume) {
        if (volume <= 3) {
            amplitude = 0.5f;
            waveCount = 10;
            wAmplitude = 1.2f;
        } else if (volume > 3 && volume < 10) {
            amplitude = 0.7f;
            waveCount = 10;
            wAmplitude = 1;
        } else if (volume > 10 && volume < 20) {
            amplitude = 0.9f;
        } else if (volume > 20) {
            waveCount = 10;
            amplitude = 1.2f;
        }
        Log.e("AA-->", "setVolume: " + amplitude + "--" + volume);

        setWaveCount(waveCount);
    }

    public void stopAnim() {
        running = false;
        waveCount = 0;

        setWaveCount(waveCount);
    }

    private void init() {
        mPaint = new Paint();
        random = new Random();

        mPaint.setAntiAlias(true);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
    }

    private void createWave() {
        if (waves == null) {
            waves = new ArrayList<>();
        }
        waves.clear();
        for (int i = 0; i < 17; i++) {
            Wave wave = new Wave();
            initAnimator(wave);
            waves.add(wave);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);

        if (!running) {
            return;
        }
        l = System.currentTimeMillis();
        Log.e(TAG, "onDraw: " + l);
        drawLine(canvas);

        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
        for (Wave wave : waves) {
            if (wave.playing) {

                wave.draw(canvas, mPaint);
            }
        }
        Log.e(TAG, "onDraw: " + (System.currentTimeMillis() - l));
        postInvalidateDelayed(20);
    }

    private void initAnimator(final Wave waveBean) {
        ValueAnimator animator = ValueAnimator.ofInt(0, waveBean.maxHeight);
        animator.setDuration(waveBean.duration);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                waveBean.waveHeight = (int) animation.getAnimatedValue();
                if (waveBean.waveHeight > waveBean.maxHeight / 2) {
                    waveBean.waveHeight = waveBean.maxHeight - waveBean.waveHeight;
                }
//                Log.e("AAA-->", "initAnimator: " + waveBean.toString());
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (waveBean.playing) {

                    waveBean.respawn();
                    initAnimator(waveBean);
                }
//                waves.remove(waveBean);
            }
        });
        animator.start();
    }

    class Wave {
        boolean playing = false;
        int maxHeight;
        int maxWidth;
        int color;
        float speed = 0.3f;
        double seed, open_class;

        int waveHeight;
        int duration;
        Paint mPaint;

        public Wave() {
            mHeight = getMeasuredHeight();
            mWidth = getMeasuredWidth();
            respawn();
        }

        public void respawn() {
            this.seed = Math.random();  // 位置
            maxWidth = (random.nextInt(mWidth / 16) + mWidth * 3 / 11);
            if (seed <= 0.2) {
                maxHeight = random.nextInt(MAX / 6) + MAX / 5;
                open_class = 2;
            } else if (seed <= 0.3 && seed > 0.2) {
                maxHeight = random.nextInt(MAX / 3) + MAX * 1 / 5;
                open_class = 3;
            } else if (seed > 0.3 && seed <= 0.7) {
                maxHeight = random.nextInt(MAX / 2) + MAX * 2 / 5;
                open_class = 3;
            } else if (seed > 0.7 && seed <= 0.8) {
                maxHeight = random.nextInt(MAX / 3) + MAX * 1 / 5;
                open_class = 3;
            } else if (seed > 0.8) {
                maxHeight = random.nextInt(MAX / 6) + MAX / 5;
                open_class = 2;
            }
            duration = random.nextInt(1000) + 1000;
            color = COLORS[random.nextInt(3)];
        }

        double equation(double i) {
            i = Math.abs(i);
            double y = -1 * amplitude
                    * Math.pow(1 / (1 + Math.pow(open_class * i, 2)), 2);
            return y;
        }

        public void draw(Canvas canvas, Paint mPaint) {
            this.mPaint = mPaint;

            this._draw(1, canvas);
        }

        private void _draw(int m, Canvas canvas) {

            Path path = new Path();
            Path pathN = new Path();
            path.moveTo(mWidth / 4, mHeight / 2);
            pathN.moveTo(mWidth / 4, mHeight / 2);
            double x_base = mWidth / 2  // 波浪位置
                    + (-mWidth / 6 + this.seed
                    * (mWidth / 3));
            double y_base = mHeight / 2;

            double x, y, x_init = 0;
            double i = -1;
            while (i <= 1) {
                x = x_base + i * maxWidth * wAmplitude;
                double function = equation(i) * waveHeight;
                y = y_base + function;
                if (x_init > 0 || x > 0) {
                    x_init = mWidth / 4;
                }
                if (y > 0.1) {
                    path.lineTo((float) x, (float) y);
                    pathN.lineTo((float) x, (float) ((float) y_base - function));
                }
                i += 0.01;
            }
            mPaint.setColor(getResources().getColor(color));
            canvas.drawPath(path, mPaint);
            canvas.drawPath(pathN, mPaint);

        }

        @Override
        public String toString() {
            return "Wave{" +
                    "maxHight=" + maxHeight +
                    ", maxWidth=" + maxWidth +
                    ", color=" + color +
                    ", speed=" + speed +
                    ", amplitude=" + amplitude +
                    ", seed=" + seed +
                    ", open_class=" + open_class +
                    ", mPaint=" + mPaint +
                    '}';
        }
    }

    private void drawLine(Canvas canvas) {
        canvas.save();
        LinearGradient shader = new LinearGradient(
                mWidth / 40, 0,
                mWidth * 39 / 40, 0,
                lineColors,
                linepositions,
                Shader.TileMode.MIRROR);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
        mPaint.setShader(shader);
        mPaint.setStrokeWidth(2);
        canvas.drawLine(mWidth / 40, mHeight / 2, mWidth * 39 / 40, mHeight / 2, mPaint);
        mPaint.setXfermode(null);
        mPaint.setShader(null);
        mPaint.clearShadowLayer();
        canvas.restore();
    }
}

本代码效果图

示例代码地址

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wljian1

你的鼓励就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值