Android最简单的方式实现波浪纹和小球

我们先看一下效果

Wave.gif

首先说明这里实现的是刚好一个周期的正弦函数,
我们来说明一下原理,我们是由左向右移动,由于刚好一个周期,所以我们将右边消失的y值正是左边新出现的y值。所以我们可以将函数的y值存储在一个数组里面,然后不断地在数组里面循环地改变不同位置的y值,那么就形成了水波纹效果。

  • 首先我们要两组组数组来存储通过正弦函数的得到的y值,和用来交换时存储的函数。
    private float[] mContentOneYs = null;//这两个代表两条线的y值得数据组
    private float[] mContentTwoys = null;
   private float[] mRestoreOnes = null;//这两个分笔试两个数组的备用数组在数组的交换的时候使用
    private float[] mRestoreTwos = null;

然后我们通过x值求出Y的数组

 for (int i = 0; i < mWidth; i++) {
            mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
            mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
        }
   private float getYPosition(int x, int swing, int offset, int baseHeight) {
        float cycle = (float) (2 * Math.PI) / mWidth;
        return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
    }

  • 说明一下这里面的几个值得意思
    x代表x值,swing代表这幅高度,offset代表一开始的sin'的角度,baseHeight代表基础高度大小,mWidth代表控件的宽度。这个在获取控件宽度的时候实现就是Onsizechang的时候。

  • 加下来就是我们移动y值了

  private void changeRestorePosition() {
        if (mWidth != 0) {
            mPosition1 = (mPosition1 + STEP1) % mWidth;//循环显示
            System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
            System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);

            mPosition2 = (mPosition2 + STEP2) % mWidth;//循环显示
            System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
            System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
        }
    }

STEP1 和 STEP2 分别代表不同的移动速度
而要绘制出图形就必须把y值和底部连成线然后由mWidht多太哦直线形成一个函数

 for (int i = 0; i < mWidth; i++) {
            final int x = i;
            final float y1 = mRestoreOnes[i];
            final float y2 = mRestoreTwos[i];
            canvas.drawLine(x, y2, x, mHeight, mPaint2);
            canvas.drawLine(x, y1, x, mHeight, mPaint1);
        }
  • 最后是不断刷新 由于人眼一般能分辨的是30fps 所以 我这里设置是睡眠0.05秒当然你不怕性能影响的话就直接在OnDraw的时候invalidate();
    先面试完整代码
public class WaveView extends View {

    private volatile boolean RUNING=true;
    private final int INIT_BASE_HEIGHT1 = 300;//基础高度
    private final int INIT_BASE_HEIGHT2 = 300;
    private int mHeight;
    private int mWidth;
    private float[] mContentOneYs = null;//这两个代表两条线的y值得数据组
    private float[] mContentTwoys = null;
    private float[] mRestoreOnes = null;//这两个分笔试两个数组的备用数组在数组的交换的时候使用
    private float[] mRestoreTwos = null;
    private static final int SWINGONE = 40;//分别代表两个的正弦函数的上下幅度
    private static final int SWINGTWO = 80;
    private static final int OFFSETONE = 0;//代表一开始的sin 的角度 从 0到360
    private static final int OFFSETTWO = 40;
    private int mPosition1 = 0;//代表起始高度
    private int mPosition2 = 0;
    private static final int STEP1 = 5;//这两个分别代表两条线的两个的速率
    private static final int STEP2 = 8;
    private Paint mPaint1;
    private Paint mPaint2;


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

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

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

    private void init() {
        mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint1.setColor(Color.parseColor("#AB9DCF"));
        mPaint1.setStrokeWidth(4);
        mPaint1.setAlpha(125);

        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setColor(Color.parseColor("#A2D1F3"));
        mPaint2.setStrokeWidth(4);
        mPaint2.setAlpha(125);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w != 0 || h != 0 || w != oldw || h != oldh) {
            mWidth = w;
            mHeight = h;
            calculatePoints();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        changeRestorePosition();
        for (int i = 0; i < mWidth; i++) {
            final int x = i;
            final float y1 = mRestoreOnes[i];
            final float y2 = mRestoreTwos[i];
            canvas.drawLine(x, y2, x, mHeight, mPaint2);
            canvas.drawLine(x, y1, x, mHeight, mPaint1);
        }
      //  invalidate();//通过这个来形成循环
    }

    private void calculatePoints() {
        mContentOneYs = new float[mWidth];
        mContentTwoys = new float[mWidth];
        mRestoreOnes = new float[mWidth];
        mRestoreTwos = new float[mWidth];
        for (int i = 0; i < mWidth; i++) {
            mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
            mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
        }
    }


    private void changeRestorePosition() {
        if (mWidth != 0) {
            mPosition1 = (mPosition1 + STEP1) % mWidth;//循环显示
            System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
            System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);

            mPosition2 = (mPosition2 + STEP2) % mWidth;//循环显示
            System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
            System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
        }
    }

    private float getYPosition(int x, int swing, int offset, int baseHeight) {
        float cycle = (float) (2 * Math.PI) / mWidth;
        return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
    }

    public void startAnimotion()
    {
        RUNING=true;
        final Handler handler=new Handler();
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                while (RUNING)
                {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            invalidate();
                        }
                    });
                }
            }
        });
        thread.start();
    }

    @Override
    protected void onDetachedFromWindow() {
        RUNING=false;
        super.onDetachedFromWindow();
    }
}

接下来我们要实现小球
我们只需要设置Paint的模式为DST_OUT,关于Paint的模式请参考
吴小龙

image

代码如下

  mPaint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            mCanvas.drawCircle(mWidth/2,mHeight/2,Math.min(mWidth,mHeight)/2,mPaint2);
            canvas.drawBitmap(bitmap,0,0,null);

然后是居中显示 文字

 Paint textPaint = new Paint();          // 创建画笔
            textPaint.setColor(0xffffffff);        // 设置颜色
            textPaint.setStyle(Paint.Style.FILL);   // 设置样式
            textPaint.setTextSize(120);
            Rect targetRect = new Rect((mWidth-mHeight)/2,  0,mHeight+(mWidth-mHeight)/2, mHeight);
//            textPaint.measureText("",0,1);
            Paint.FontMetricsInt fontMetrics = mPaint2.getFontMetricsInt();
            int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
            textPaint.setTextAlign(Paint.Align.CENTER);
            canvas.drawText(value+"分", targetRect.centerX(), baseline+50, textPaint);

同时我们在xml里给他设置一个background
最后得到的效果如下

GIF3.gif

最后代码如下

public class WaveView extends View {
    private int value=80;
    private boolean drawCircle=true;
    private volatile boolean RUNING=true;
    private final int INIT_BASE_HEIGHT1 = 300;//基础高度
    private final int INIT_BASE_HEIGHT2 = 300;
    private int mHeight;
    private int mWidth;
    private float[] mContentOneYs = null;//这两个代表两条线的y值得数据组
    private float[] mContentTwoys = null;
    private float[] mRestoreOnes = null;//这两个分笔试两个数组的备用数组在数组的交换的时候使用
    private float[] mRestoreTwos = null;
    private static final int SWINGONE = 40;//分别代表两个的正弦函数的上下幅度
    private static final int SWINGTWO = 80;
    private static final int OFFSETONE = 0;//代表一开始的sin 的角度 从 0到360
    private static final int OFFSETTWO = 40;
    private int mPosition1 = 0;//代表起始高度
    private int mPosition2 = 0;
    private static final int STEP1 = 5;//这两个分别代表两条线的两个的速率
    private static final int STEP2 = 8;
    private Paint mPaint1;
    private Paint mPaint2;
    private Paint circlePaint;
    Bitmap bitmap;

    Canvas mCanvas;

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

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

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

    private void init() {
        mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint1.setColor(Color.parseColor("#AB9DCF"));
        mPaint1.setStrokeWidth(4);
        mPaint1.setAlpha(125);

        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setColor(Color.parseColor("#A2D1F3"));
        mPaint2.setStrokeWidth(4);
        mPaint2.setAlpha(125);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        bitmap = Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(bitmap);
        mCanvas.drawColor(Color.WHITE);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w != 0 || h != 0 || w != oldw || h != oldh) {
            mWidth = w;
            mHeight = h;
            calculatePoints();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        init();
        changeRestorePosition();
        for (int i = 0; i < mWidth; i++) {
            final int x = i;
            final float y1 = mRestoreOnes[i];
            final float y2 = mRestoreTwos[i];
            canvas.drawLine(x, y2, x, mHeight, mPaint2);
            canvas.drawLine(x, y1, x, mHeight, mPaint1);
        }
      //  invalidate();//通过这个来形成循环

        if(drawCircle)
        {

            Paint textPaint = new Paint();          // 创建画笔
            textPaint.setColor(0xffffffff);        // 设置颜色
            textPaint.setStyle(Paint.Style.FILL);   // 设置样式
            textPaint.setTextSize(120);
            Rect targetRect = new Rect((mWidth-mHeight)/2,  0,mHeight+(mWidth-mHeight)/2, mHeight);
//            textPaint.measureText("",0,1);
            Paint.FontMetricsInt fontMetrics = mPaint2.getFontMetricsInt();
            int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
            textPaint.setTextAlign(Paint.Align.CENTER);
            canvas.drawText(value+"分", targetRect.centerX(), baseline+50, textPaint);
        }
        if (drawCircle ) {
            mPaint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            mCanvas.drawCircle(mWidth/2,mHeight/2,Math.min(mWidth,mHeight)/2,mPaint2);
            canvas.drawBitmap(bitmap,0,0,null);
        }
    }

    private void calculatePoints() {
        mContentOneYs = new float[mWidth];
        mContentTwoys = new float[mWidth];
        mRestoreOnes = new float[mWidth];
        mRestoreTwos = new float[mWidth];
        for (int i = 0; i < mWidth; i++) {
            mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
            mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
        }
    }


    private void changeRestorePosition() {
        if (mWidth != 0) {
            mPosition1 = (mPosition1 + STEP1) % mWidth;//循环显示
            System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
            System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);

            mPosition2 = (mPosition2 + STEP2) % mWidth;//循环显示
            System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
            System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
        }
    }

    private float getYPosition(int x, int swing, int offset, int baseHeight) {
        float cycle = (float) (2 * Math.PI) / mWidth;
        return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
    }

    public void startAnimotion()
    {
        RUNING=true;
        final Handler handler=new Handler();
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                while (RUNING)
                {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            invalidate();
                        }
                    });
                }
            }
        });
        thread.start();
    }

    @Override
    protected void onDetachedFromWindow() {
        RUNING=false;
        super.onDetachedFromWindow();
    }
}

你可能感兴趣的:(Android最简单的方式实现波浪纹和小球)