【Android】声浪 UI 效果并附上详细代码

声浪效果是基于第三方实现的。
https://github.com/xfans/VoiceWaveView
将三方的 Kotlin 代码转 java 使用(按照他的readme 进行依赖,好像少了点东西,至少本项目跑不起来)

声浪效果在android 8 以上都是比较好的,不会出现断点的情况。但是在 android 8下,就会出现如下图所示的断点情况。
【Android】声浪 UI 效果并附上详细代码_第1张图片

主类

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;

import androidx.annotation.Nullable;

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


/**
 * 音浪线
 */
public class VoiceWaveView extends View {

    private static final String TAG = "VoiceWaveView";
    private List<Integer> bodyWaveList = new ArrayList<>();
    private List<Integer> headerWaveList = new ArrayList<>();
    private List<Integer> footerWaveList = new ArrayList<>();
    private List<Integer> waveList = new ArrayList<>();

    private float lineSpace = 10f;
    private float lineWidth = 20f;
    private long duration = 200;
    private int lineColor = Color.BLUE;
    private Paint paintLine;
    private Paint paintPathLine;
    private ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
    private float valueAnimatorOffset = 1f;
    private Handler valHandler = new Handler();
    private Path linePath = new Path();
    private boolean isStart = false;
    private WaveMode waveMode = WaveMode.UP_DOWN;
    private LineType lineType = LineType.BAR_CHART;
    private int showGravity = Gravity.LEFT | Gravity.BOTTOM;
    private Runnable runnable;

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

    public VoiceWaveView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VoiceWaveView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(@Nullable AttributeSet attrs) {
        if (attrs != null) {
            // Read and initialize attributes here
        }

        paintLine = new Paint();
        paintLine.setAntiAlias(true);
        paintLine.setStrokeCap(Paint.Cap.ROUND);

        paintPathLine = new Paint();
        paintPathLine.setAntiAlias(true);
        paintPathLine.setStyle(Paint.Style.STROKE);

        valueAnimator.addUpdateListener(animation -> {
            valueAnimatorOffset = (float) animation.getAnimatedValue();
            invalidate();
        });
    }

    public void setLineSpace(float lineSpace) {
        this.lineSpace = lineSpace;
    }

    public void setLineWidth(float lineWidth) {
        this.lineWidth = lineWidth;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    public void setWaveMode(WaveMode waveMode) {
        this.waveMode = waveMode;
    }

    public void setLineType(LineType lineType) {
        this.lineType = lineType;
    }

    public void setShowGravity(int showGravity) {
        this.showGravity = showGravity;
    }

    public VoiceWaveView addBody(int soundLevel) {
        checkNum(soundLevel);
        bodyWaveList.add(soundLevel);
        return this;
    }

    public VoiceWaveView initBody(int length, int soundLevel) {
        bodyWaveList.clear();
        for (int i = 0; i < length; i++) {
            addBody(soundLevel);
        }
        return this;
    }

    // TODO: 2023/11/1 中间弹的的逻辑
    public VoiceWaveView refreshBody(int soundLevel) {
        // 添加 soundLevel 到头部
        bodyWaveList.add(0, soundLevel);

        // 递减相邻元素的值
        for (int i = 1; i < bodyWaveList.size() - 1; i++) {
            int previousValue = bodyWaveList.get(i - 1);
            int currentValue = bodyWaveList.get(i);
            int nextValue = bodyWaveList.get(i + 1);

            int updatedValue = Math.max(currentValue - 1, Math.max(previousValue, nextValue) - 2);
            bodyWaveList.set(i, updatedValue);
        }

        return this;
    }

    /**
     * 刷新最后一个
     *
     * @param soundLevel
     */
    public void updateBody(int soundLevel) {
        bodyWaveList.remove(bodyWaveList.size() - 1);
        addBody(soundLevel);
    }

    public VoiceWaveView addHeader(int soundLevel) {
        checkNum(soundLevel);
        headerWaveList.add(soundLevel);
        return this;
    }

    public VoiceWaveView addFooter(int soundLevel) {
        checkNum(soundLevel);
        footerWaveList.add(soundLevel);
        return this;
    }

    private void checkNum(int soundLevel) {
        if (soundLevel < 0 || soundLevel > 100) {
            throw new IllegalArgumentException("num must be between 0 and 100");
        }
    }

    public void start() {
        if (isStart) {
            return;
        }
        L.i(TAG, "start ");
        isStart = true;
        if (waveMode == WaveMode.UP_DOWN) {
            valueAnimator.setDuration(duration);
            valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
            valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            valueAnimator.start();
        } else if (waveMode == WaveMode.LEFT_RIGHT) {
            runnable = new Runnable() {
                @Override
                public void run() {
                //日志类,自己构建即可
                    L.i(TAG, bodyWaveList.toString());
                    Integer last = bodyWaveList.remove(bodyWaveList.size() - 1);
                    bodyWaveList.add(0, last);
                    invalidate();
                    valHandler.postDelayed(this, duration);
                }
            };
            valHandler.post(runnable);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        L.i(TAG, "onDraw ");

        waveList.clear();
        waveList.addAll(headerWaveList);
        waveList.addAll(bodyWaveList);
        waveList.addAll(footerWaveList);

        linePath.reset();
        paintPathLine.setStrokeWidth(lineWidth);
        paintPathLine.setColor(lineColor);

        paintLine.setStrokeWidth(lineWidth);
        paintLine.setColor(lineColor);

        float measuredWidth = getMeasuredWidth();
        float measuredHeight = getMeasuredHeight();

        float startX = 0f;
        float startY = 0f;
        float endX = 0f;
        float endY = 0f;

        for (int i = 0; i < waveList.size(); i++) {
            float offset = 1f;
            if (i >= headerWaveList.size() && i < (waveList.size() - footerWaveList.size())) {
                offset = valueAnimatorOffset;
            }

            float lineHeight = (waveList.get(i) / 100.0f) * measuredHeight * offset;

            int absoluteGravity = Gravity.getAbsoluteGravity(showGravity, getLayoutDirection());

            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.CENTER_HORIZONTAL:
                    int lineSize = waveList.size();
                    float allLineWidth = lineSize * (lineSpace + lineWidth);
                    if (allLineWidth < measuredWidth) {
                        startX = (i * (lineSpace + lineWidth) + lineWidth / 2) + ((measuredWidth - allLineWidth) / 2);
                    } else {
                        startX = i * (lineSpace + lineWidth) + lineWidth / 2;
                    }
                    endX = startX;
                    break;

                case Gravity.RIGHT:
                    lineSize = waveList.size();
                    allLineWidth = lineSize * (lineSpace + lineWidth);
                    if (allLineWidth < measuredWidth) {
                        startX = (i * (lineSpace + lineWidth) + lineWidth / 2) + (measuredWidth - allLineWidth);
                    } else {
                        startX = i * (lineSpace + lineWidth) + lineWidth / 2;
                    }
                    endX = startX;
                    break;

                case Gravity.LEFT:
                    startX = i * (lineSpace + lineWidth) + lineWidth / 2;
                    endX = startX;
                    break;
            }

            switch (showGravity & Gravity.VERTICAL_GRAVITY_MASK) {
                case Gravity.TOP:
                    startY = 0f;
                    endY = lineHeight;
                    break;

                case Gravity.CENTER_VERTICAL:
                    startY = (measuredHeight / 2 - lineHeight / 2);
                    endY = (measuredHeight / 2 + lineHeight / 2);
                    break;

                case Gravity.BOTTOM:
                    startY = (measuredHeight - lineHeight);
                    endY = measuredHeight;
                    break;
            }

            if (lineType == LineType.BAR_CHART) {
                canvas.drawLine(startX, startY, endX, endY, paintLine);
            }

            if (lineType == LineType.LINE_GRAPH) {
                if (i == 0) {
                    linePath.moveTo(startX, startY);
                    float pathEndX = endX + (lineWidth / 2) + (lineSpace / 2);
                    linePath.lineTo(pathEndX, endY);
                } else {
                    linePath.lineTo(startX, startY);
                    float pathEndX = endX + (lineWidth / 2) + (lineSpace / 2);
                    linePath.lineTo(pathEndX, endY);
                }
            }
        }

        if (lineType == LineType.LINE_GRAPH) {
            canvas.drawPath(linePath, paintPathLine);
        }
    }

    public void stop() {
        L.i(TAG, "stop ");
        isStart = false;
        if (runnable != null) {
            valHandler.removeCallbacks(runnable);
        }
        valueAnimator.cancel();
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        // TODO onSaveInstanceState
        return super.onSaveInstanceState();
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        // TODO onRestoreInstanceState
        super.onRestoreInstanceState(state);
    }
}

相关枚举类

public enum LineType {
    LINE_GRAPH(0),
    BAR_CHART(1);
    private int value;

    private LineType(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

public enum WaveMode {
    UP_DOWN,
    LEFT_RIGHT
}

你可能感兴趣的:(android代码小结,知识分享,android,ui)