package com.gelitenight.waveview.library;

import android.content.Context;
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.util.AttributeSet;
import android.view.View;

public class WaveView extends View {
    private static final float DEFAULT_AMPLITUDE_RATIO = 0.05f;
    private static final float DEFAULT_WATER_LEVEL_RATIO = 0.5f;
    private static final float DEFAULT_WAVE_LENGTH_RATIO = 1.0f;
    private static final float DEFAULT_WAVE_SHIFT_RATIO = 0.0f;

    // if true, the shader will display the wave
    private boolean mShowWave;

    // shader containing repeated waves
    private BitmapShader mWaveShader;
    // shader matrix
    private Matrix mShaderMatrix;
    // paint containers wave shader
    private Paint mViewPaint;

    private float mDefaultAmplitude;
    private float mDefaultWaterLevel;
    private double mDefaultAngularFrequency;

    private float mAmplitudeRatio = DEFAULT_AMPLITUDE_RATIO;
    private float mWaveLengthRatio = DEFAULT_WAVE_LENGTH_RATIO;
    private float mWaterLevelRatio = DEFAULT_WATER_LEVEL_RATIO;
    private float mWaveShiftRatio = DEFAULT_WAVE_SHIFT_RATIO;

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

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

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

    private void init() {
        mShaderMatrix = new Matrix();
        mViewPaint = new Paint();
    }

    public float getWaveShiftRatio() {
        return mWaveShiftRatio;
    }

    public void setWaveShiftRatio(float waveShiftRatio) {
        if (mWaveShiftRatio != waveShiftRatio) {
            mWaveShiftRatio = waveShiftRatio;
            invalidate();
        }
    }

    public float getWaterLevelRatio() {
        return mWaterLevelRatio;
    }

    public void setWaterLevelRatio(float waterLevelRatio) {
        if (mWaterLevelRatio != waterLevelRatio) {
            mWaterLevelRatio = waterLevelRatio;
            invalidate();
        }
    }

    public float getAmplitudeRatio() {
        return mAmplitudeRatio;
    }

    public void setAmplitudeRatio(float amplitudeRatio) {
        if (mAmplitudeRatio != amplitudeRatio) {
            mAmplitudeRatio = amplitudeRatio;
            invalidate();
        }
    }

    public boolean isShowWave() {
        return mShowWave;
    }

    public void setShowWave(boolean showWave) {
        this.mShowWave = showWave;
    }

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

        createShader();
    }

    /**
     * Create the shader with default waves which repeat horizontally, and clamp colors vertically
     */
    private void createShader() {
        mDefaultAngularFrequency = 2.0f * Math.PI / DEFAULT_WAVE_LENGTH_RATIO / getWidth();
        mDefaultAmplitude = getHeight() * DEFAULT_AMPLITUDE_RATIO;
        mDefaultWaterLevel = getHeight() * DEFAULT_WATER_LEVEL_RATIO;

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

        // Draw default waves into the bitmap
        // y=Asin(ωx+φ)+h
        int waveX1 = 0;
        final int wave2Shift = getWidth() / 4;
        final int endX = getWidth();
        final int endY = getHeight();

        final Paint wavePaint1 = new Paint();
        wavePaint1.setColor(Color.WHITE);
        wavePaint1.setAlpha(40);

        final Paint wavePaint2 = new Paint();
        wavePaint2.setColor(Color.WHITE);
        wavePaint2.setAlpha(60);

        while (waveX1 < endX) {
            int waveX2 = (waveX1 + wave2Shift) % endX;
            double wx = waveX1 * mDefaultAngularFrequency;

            // draw behind wave with the alpha 40
            int startY = (int) (mDefaultWaterLevel + mDefaultAmplitude * Math.sin(wx));

            canvas.drawLine(waveX1, startY, waveX1, endY, wavePaint1);
            canvas.drawLine(waveX2, startY, waveX2, endY, wavePaint2);

            waveX1++;
        }

        // use the bitamp to create the shader
        mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
        mViewPaint.setShader(mWaveShader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // modify paint shader according to mShowWave state
        if (mShowWave && mWaveShader != null) {
            // first call after mShowWave, assign it to our paint
            if (mViewPaint.getShader() == null) {
                mViewPaint.setShader(mWaveShader);
            }

            // translate shader accordingly to maskX maskY positions
            // maskY is affected by the offset to vertically center the wave
            mShaderMatrix.setScale(
                    mWaveLengthRatio / DEFAULT_WAVE_LENGTH_RATIO,
                    mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO,
                    0,
                    mDefaultWaterLevel);
            mShaderMatrix.postTranslate(
                    mWaveShiftRatio * getWidth(),
                    (DEFAULT_WATER_LEVEL_RATIO - mWaterLevelRatio) * getHeight());

            // assign matrix to invalidate the shader
            mWaveShader.setLocalMatrix(mShaderMatrix);
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mViewPaint);
        } else {
            mViewPaint.setShader(null);
        }

    }
}

@@ -4030,58 +4030,8 @@
 y;%0A%0A
-            // draw behind wave with the alpha 40%0A
     
@@ -4111,24 +4111,74 @@
 .sin(wx));%0A%0A
+            // draw bottom wave with the alpha 40%0A
             
@@ -4236,16 +4236,63 @@
 aint1);%0A
+            // draw top wave with the alpha 60%0A
         
@@ -4946,16 +4946,12 @@
  // 
-translat
+sacl
 e sh
@@ -4968,112 +4968,150 @@
 ding
-ly
  to m
-askX maskY positions%0A            // maskY is affected by the offset to vertically cente
+WaveLengthRatio and mAmplitudeRatio%0A            // this decides the size(mWaveLengthRatio for width, mAmplitudeRatio fo
 r 
-t
 he
+ight) of
  wave
+s
 %0A   
@@ -5332,24 +5332,209 @@
 aterLevel);%0A
+            // translate shader according to mWaveShiftRatio and mWaterLevelRatio%0A            // this decides the start position(mWaveShiftRatio for x, mWaterLevelRatio for y) of waves%0A
             
@@ -5805,16 +5805,17 @@
 atrix);%0A
+%0A
         

add comments