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();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容