当前位置: 首页 > 工具软件 > WaveView > 使用案例 >

【Android】 水波纹WaveView

彭俊智
2023-12-01
package com.qiao.atc.custom.view.path;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.qiao.atc.R;


/**
 * Created by qiao9 on 2018/6/14.
 * 波浪图形
 */
public class WaveView extends View {

    /**
     * +------------------------+
     * |<--wave length->        |______
     * |   /\          |   /\   |  |
     * |  /  \         |  /  \  | amplitude
     * | /    \        | /    \ |  |
     * |/      \       |/      \|__|____
     * |        \      /        |  |
     * |         \    /         |  |
     * |          \  /          |  |
     * |           \/           | water level
     * |                        |  |
     * |                        |  |
     * +------------------------+__|____
     */
    //默认振幅比
    private static final float DEFAULT_AMPLITUDE_RATIO = 0.06f;
    //默认水位比
    private static final float DEFAULT_WATER_LEVEL_RATIO = 0.5f;
    //默认波长比
    private static final float DEFAULT_WAVE_LENGTH_RATIO = 0.5f;
    //默认波转变比率
    private static final float DEFAULT_WAVE_SHIFT_RATIO = 0.0f;


    //默认后面的波颜色
    public static int DEFAULT_BEHIND_WAVE_COLOR;
    //默认前面的波颜色
    public static int DEFAULT_FRONT_WAVE_COLOR;
    public static final ShapeType DEFAULT_WAVE_SHAPE = ShapeType.SQUARE;

    //如果为真则显示波浪
    private boolean mShowWave;
    //着色漆包含的重复的波形
    private BitmapShader mWaveShader;
    //矩阵
    private Matrix mShaderMatrix;
    //Wave画笔
    private Paint mViewPaint;
    //边线画笔
    private Paint mBorderPaint;

    //默认振幅
    private float mDefaultAmplitude;
    //默认水位
    private float mDefaultWaterLevel;
    //默认波长
    private float mDefaultWaveLenght;
    //默认角频率
    private double mDefaultAngularFrequency;

    //振幅比
    private float mAmplitudeRatio = DEFAULT_AMPLITUDE_RATIO;
    //波长比
    private float mWaveLenghtRatio = DEFAULT_WAVE_LENGTH_RATIO;
    //水位比
    private float mWaterLevelRatio = DEFAULT_WATER_LEVEL_RATIO;
    //波转比率
    private float mWaveShiftRatio = DEFAULT_WAVE_SHIFT_RATIO;

    //颜色啊
    private int mBehindWaveColor = DEFAULT_BEHIND_WAVE_COLOR;
    private int mFrontWaveColor = DEFAULT_FRONT_WAVE_COLOR;


    public enum ShapeType {
        CIRCLE,
        SQUARE
    }

    private ShapeType mShapeType = DEFAULT_WAVE_SHAPE;

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

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


    private void init(Context context, AttributeSet attrs) {
        mShaderMatrix = new Matrix();
        mViewPaint = new Paint();
        mViewPaint.setAntiAlias(true);
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ScrollCircleColorChange, 0, 0);
        mBehindWaveColor = array.getColor(R.styleable.ScrollCircleColorChange_firstColor, Color.parseColor("#069adc"));
        mFrontWaveColor = array.getColor(R.styleable.ScrollCircleColorChange_secondColor, Color.parseColor("#41c1f4"));
        array.recycle();
    }


    public void setFirstColor(int firstColor) {
        DEFAULT_FRONT_WAVE_COLOR = firstColor;
    }

    public void setThreeColor(int threeColor) {
//        mThreeColor = threeColor;
    }

    public void setSecondColor(int sceondColor) {
        DEFAULT_BEHIND_WAVE_COLOR = sceondColor;
    }

    public float getWaveShiftRatio() {
        return mWaveShiftRatio;
    }

    /**
     * 根据波转化比移动波形
     *
     * @param waveShiftRatio 变换范围0~1,默认为0
     *                       波变换比的结果,波的倍数,宽度是要移的长度。
     */
    public void setWaveShiftRatio(float waveShiftRatio) {
        if (mWaveShiftRatio != waveShiftRatio) {
            mWaveShiftRatio = waveShiftRatio;
            invalidate();
        }
    }

    public float getWaterLevelRatio() {
        return mWaterLevelRatio;
    }

    /**
     * 根据水位比设置水位
     *
     * @param waterLevelRatio Should be 0 ~ 1. Default to be 0.5.
     *                        <br/>Ratio of water level to WaveView height.
     */
    public void setWaterLevelRatio(float waterLevelRatio) {
        if (mWaterLevelRatio != waterLevelRatio) {
            mWaterLevelRatio = waterLevelRatio;
            invalidate();
        }
    }

    public float getAmplitudeRatio() {
        return mAmplitudeRatio;
    }

    /**
     * 根据振幅比设置波的波动大小
     *
     * @param amplitudeRatio 默认是0.05 放大倍数+水平度数应该小于1
     *                       波图振幅与高度之比
     */
    public void setAmplitudeRatio(float amplitudeRatio) {
        if (mAmplitudeRatio != amplitudeRatio) {
            mAmplitudeRatio = amplitudeRatio;
            invalidate();
        }
    }

    public float getWaveLengthRatio() {
        return mWaveLenghtRatio;
    }

    public void setWaveLengthRatio(float waveLengthRatio) {
        mWaveLenghtRatio = waveLengthRatio;
    }

    /**
     * 判断波是否显示
     *
     * @return
     */
    public boolean isShowWave() {
        return mShowWave;
    }

    /**
     * 设置是否显示波
     *
     * @param showWave
     */
    public void setShowWave(boolean showWave) {
        mShowWave = showWave;
    }

    /**
     * 设置边缘线条画笔
     *
     * @param width 画笔宽度
     * @param color 画笔颜色
     */
    public void setBorder(int width, int color) {
        if (mBorderPaint == null) {
            mBorderPaint = new Paint();
            mBorderPaint.setAntiAlias(true);
            mBorderPaint.setStyle(Paint.Style.STROKE);
        }
        mBorderPaint.setColor(color);
        mBorderPaint.setStrokeWidth(width);

        invalidate();
    }

    /**
     * 在控件大小发生改变时会调用,初始化会调用一次
     *
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        createShader();
    }

    /**
     * 创建带有默认波浪的着色器,水平重复,垂直夹住
     */
    private void createShader() {
        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }
        mBehindWaveColor = DEFAULT_BEHIND_WAVE_COLOR;
        mFrontWaveColor = DEFAULT_FRONT_WAVE_COLOR;
        mDefaultAngularFrequency = 2.0f * Math.PI / DEFAULT_WAVE_LENGTH_RATIO / getWidth();
        mDefaultAmplitude = getHeight() * DEFAULT_AMPLITUDE_RATIO;
        mDefaultWaterLevel = getHeight() * 0.8f;
        mDefaultWaveLenght = getWidth();

        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        Paint wavePaint = new Paint();
        wavePaint.setStrokeWidth(1);
        wavePaint.setAntiAlias(true);

        //在位图中绘制默认波//y=Asin(ωx+φ)+h
        final int endX = getWidth() - 1;
//        final int endY = getHeight() + 1;

        float[] waveY = new float[endX];

        wavePaint.setColor(mBehindWaveColor);
        for (int beginX = 0; beginX < endX; beginX++) {
            double wx = beginX * mDefaultAngularFrequency;
            float beginY = (float) (mDefaultAmplitude + mDefaultWaterLevel * Math.sin(wx));
//            canvas.drawLine(beginX,beginY,beginX,endY,wavePaint);
            canvas.drawLine(beginX, 0, beginX, beginY, wavePaint);

            waveY[beginX] = beginY;
        }

        wavePaint.setColor(mFrontWaveColor);
        final int wave2Shift = (int) (mDefaultWaveLenght / 8);
        for (int beginX = 0; beginX < endX; beginX++) {
//            canvas.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, endY, wavePaint);
            canvas.drawLine(beginX, 0, beginX, waveY[(beginX + wave2Shift) % endX], wavePaint);
        }

        wavePaint.setColor(mFrontWaveColor);
        wavePaint.setAlpha(60);
        final int wave3Shift = (int) (mDefaultWaveLenght / 3);
        for (int beginX = 0; beginX < endX; beginX++) {
//            canvas.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, endY, wavePaint);
            canvas.drawLine(beginX, 0, beginX, waveY[(beginX + wave3Shift) % endX], wavePaint);
        }

        //使用位图创建着色器
        mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
        mViewPaint.setShader(mWaveShader);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        //根据shader的状态修改着色器
        if (mShowWave && mWaveShader != null) {
            //首先调用showWave之后,将其分配给画笔
            if (mViewPaint.getShader() == null) {
                mViewPaint.setShader(mWaveShader);
            }
            //将矩阵设置为根据波长比和振幅比来缩放//这决定了波浪的大小
            mShaderMatrix.setScale(mWaveLenghtRatio / DEFAULT_WAVE_LENGTH_RATIO, mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO, 0, mDefaultWaterLevel);
            //用指定的波转比和水位比来处理矩阵//这决定了波浪的起始位置
            //mShaderMatrix.postTranslate(mWaveShiftRatio * getWidth(), (DEFAULT_WATER_LEVEL_RATIO - mWaterLevelRatio) * getHeight());
            //M' = T(dx, dy) * M
            mShaderMatrix.postTranslate(mWaveShiftRatio * getWidth(), (0.1f) * getHeight());
            //设置着色器的局部矩阵
            mWaveShader.setLocalMatrix(mShaderMatrix);

            float borderWidth = mBorderPaint == null ? 0f : mBorderPaint.getStrokeWidth();
            switch (mShapeType) {
                case CIRCLE:
                    if (borderWidth > 0) {
                        canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, (getWidth() - borderWidth) / 2f - 1f, mBorderPaint);
                    }
                    float radius = getWidth() / 2f - borderWidth;
                    canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mViewPaint);
                    break;

                case SQUARE:
                    if (borderWidth > 0) {
                        canvas.drawRect(borderWidth / 2f, borderWidth / 2f, getWidth() - borderWidth / 2f - 0.5f, getHeight() - borderWidth / 2f - 0.5f, mBorderPaint);
                    }
                    canvas.drawRect(borderWidth, borderWidth, getWidth() - getHeight(), getHeight() - borderWidth, mViewPaint);
                    break;
            }
        } else {
            mViewPaint.setShader(null);
        }

    }
}

 类似资料: