Android自定义控件练手——波浪效果

2018-02-12 18:55:44来源:cnblogs.com作者:SteinsGateZero人点击

分享

    这一次要绘制出波浪效果,也是小白的我第一次还望轻喷。首先当然是展示效果图啦:

    一.首先来说说实现思路。

    想到波浪效果,当然我第一反应是用正余弦波来设计啦(也能通过贝塞尔曲线,这里我不提及这个方法但是在demo里这种方法也实现了),肯定要绘制一个静态的波,然后通过不断的对它平移刷新,这样最简单的波浪效果就有了,如果再给它加一个比它提前一定周期的波一起平移,那不是波浪效果的层次就有了。

    二.绘制。

    首先要绘制一个静态的波形图,嗨呀说来简单但是怎么画呢,不要慌先看下面这张丑图:

    通过上面的图我们发现曲线被切分成了无数的竖线,我们可以知道波浪的高低,就是波峰与波谷,根据函数公式,通过不断的绘制竖线,就能得到一个静态波形图。代码如下:

1  //在宽度以内绘制一条条竖线2         while (drawPoint.x < mWidth) {3             //第一条波的y坐标4             drawPoint.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum));5             canvas.drawLine(drawPoint.x, drawPoint.y, drawPoint.x, mHeight, mPaint);6             //跳到下一个点继续7             drawPoint.x++;8         }

   这里我们要注意绘制的默认坐标系如下图:

   画出静态的波形图之后,我们只要每次让这个波向前或者向后移动一定周期再不断刷新,就能出现波浪效果了。重写view的ondraw方法就有如下:

 1 drawPoint.x = 0;//重置为0,从原点开始绘制 2         Double rightperiod = Math.PI / 8 * count;//每次平移Math.PI/8个周期 3         if (count == 16) {//每次平移Math.PI/8个周期,平移第16次,平移了一个完整的周期 4             count = 0;//平移了一个完整周期归零重新开始计数 5         } else { 6             count++; 7         } 8  9         //在宽度以内绘制一条条竖线10         while (drawPoint.x < mWidth) {11             //第一条波的y坐标12             drawPoint.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum - rightperiod));13             //绘制最上面显示主波的竖线14             canvas.drawLine(drawPoint.x, drawPoint.y, drawPoint.x, mHeight, mPaint);15             //跳到下一个点继续16             drawPoint.x++;17         }18         //定时更新19         postInvalidateDelayed(17);

   这样一条会动的波浪就基本完成了,主体功能基本实现,之后再另外画一个平移一定周期的波浪并且通过用属性动画ValueAnimator对波浪波峰波谷与水位变化的调控就能达到上面的效果。下面是完整的代码:

  1 public class WaveFunctionView extends View {  2     private Path mPath;//路径  3     private Paint mPaint, mPaintMore;//画笔  4     private PointF drawPoint, drawPoint2;//绘制点  5     private ValueAnimator animator, animatorh;  6     private float mWidth, mHeight, waveHeight;//控件宽,控件高,水位  7     private float waveDeepmin = 8f;//最小的波峰与波谷  8     private float waveDeepMax = 20f;//最大的波峰与波谷  9     private float waveDeep = 8f;//波峰与波谷 10     private float arcRa = 0;//圆半径 11     private Boolean iscircle = true;//是否是圆形图案 12     private Boolean antiAlias = true;//是否开启抗锯齿 13     public String MAINCOLOR_DEF = "#0000AA", NEXTCOLOR_DEF = "#0000FF";//默认颜色 14     private int mainColor = Color.parseColor(MAINCOLOR_DEF), nextColor = Color.parseColor(NEXTCOLOR_DEF);//颜色 15     private Double anglenum = Math.PI / 180; 16     private int count = 0;//绘制次数 17  18     public WaveFunctionView(Context context) { 19         super(context); 20         init(); 21     } 22  23     public WaveFunctionView(Context context, AttributeSet attrs, int defStyleAttr) { 24         super(context, attrs, defStyleAttr); 25         init(); 26     } 27  28     public WaveFunctionView(Context context, AttributeSet attrs) { 29         super(context, attrs); 30         init(); 31     } 32  33     @Override 34     protected void onSizeChanged(int w, int h, int oldw, int oldh) { 35         mWidth = w;//获得控件宽度 36         mHeight = h;//获得控件高度 37         if (mWidth > mHeight) {//若要裁剪为圆形,以最短的长度为直径 38             arcRa = mHeight / 2; 39             if (iscircle) { 40                 mWidth = mHeight; 41             } 42         } else { 43             arcRa = mWidth / 2; 44             if (iscircle) { 45                 mHeight = mWidth; 46             } 47         } 48         waveHeight = mHeight;//初始化开始水位 49         ChangeWaveLevel(5); 50         super.onSizeChanged(w, h, oldw, oldh); 51     } 52  53     //是否是圆形 54     public void isCircle(Boolean iscircle) { 55         this.iscircle = iscircle; 56     } 57  58     //是否开启抗锯齿 59     public void setAntiAlias(Boolean antiAlias) { 60         this.antiAlias = antiAlias; 61         mPaint.setAntiAlias(antiAlias); 62         mPaintMore.setAntiAlias(antiAlias); 63     } 64  65     //设置主波颜色 66     public void setMainWaveColor(int color) { 67         mainColor = color; 68         mPaint.setColor(color); 69     } 70  71     //设置被遮挡的副波颜色 72     public void setSecondaryWaveColor(int color) { 73         nextColor = color; 74         mPaintMore.setColor(color); 75     } 76  77     @Override 78     protected void onDraw(Canvas canvas) { 79         // TODO Auto-generated method stub 80         super.onDraw(canvas); 81         if (iscircle) {//判断是否定义为圆形 82             mPath.reset();//重置路径 83             mPath.addCircle(arcRa, arcRa, arcRa, Path.Direction.CW);//画以(arcRa,arcRa),半径为arcRa的顺时针的圆 84             canvas.clipPath(mPath);//裁剪 85         } 86         drawPoint.x = 0;//重置为0,从原点开始绘制 87         Double rightperiod = Math.PI / 8 * count;//每次平移Math.PI/8个周期 88         if (count == 16) {//每次平移Math.PI/8个周期,平移第16次,平移了一个完整的周期 89             count = 0;//平移了一个完整周期归零重新开始计数 90         } else { 91             count++; 92         } 93  94         //在宽度以内绘制一条条竖线 95         while (drawPoint.x < mWidth) { 96             //第一条波的y坐标 97             drawPoint.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum - rightperiod)); 98             //第二条波的y坐标,比第一条向右移动了Math.PI/2个周期 99             drawPoint2.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum - rightperiod - Math.PI / 2));100             //先绘制被遮挡的副波的竖线101             canvas.drawLine(drawPoint.x, drawPoint2.y, drawPoint.x, mHeight, mPaintMore);102             //绘制最上面显示主波的竖线103             canvas.drawLine(drawPoint.x, drawPoint.y, drawPoint.x, mHeight, mPaint);104             //跳到下一个点继续105             drawPoint.x++;106         }107         //定时更新108         postInvalidateDelayed(17);109     }110 111     private void init() {112         mPath = new Path();113         mPaint = new Paint();114         mPaint.setColor(mainColor);//设置颜色115         mPaint.setAntiAlias(antiAlias);//抗锯齿(性能影响)116         mPaint.setStyle(Paint.Style.FILL);117         mPaint.setAlpha(50);118         mPaintMore = new Paint();119         mPaintMore.setAntiAlias(antiAlias);//抗锯齿120         mPaintMore.setStyle(Paint.Style.FILL);121         mPaintMore.setColor(nextColor);//设置颜色122         mPaintMore.setAlpha(30);123         drawPoint = new PointF(0, 0);124         drawPoint2 = new PointF(0, 0);125     }126 127     public void ChangeWaveLevel(int percent) {128         animator = ValueAnimator.ofFloat(waveDeepmin, waveDeepMax);//设置属性值变化范围,最大波峰波谷与最小129         animator.setDuration(1000);//设置动画时间130         animator.setInterpolator(new LinearInterpolator());//控制动画的变化速率131         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {132             @Override133             public void onAnimationUpdate(ValueAnimator animation) {134                 waveDeep = (float) animation.getAnimatedValue();135             }136         });137         animator.setRepeatMode(ValueAnimator.REVERSE);//往返模式138         animator.setRepeatCount(1);139         animatorh = ValueAnimator.ofFloat(waveHeight, mHeight * (10 - percent) / 10);//水位变化140         animatorh.setDuration(2000);//设置动画时间141         animatorh.setInterpolator(new DecelerateInterpolator());//控制动画的变化速率142 143         animatorh.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {144             @Override145             public void onAnimationUpdate(ValueAnimator animation) {146                 waveHeight = (float) animation.getAnimatedValue();147             }148         });149         animator.start();//开始动画150         animatorh.start();//开始动画151     }152 }
完整代码

GitHub:https://github.com/SteinsGateZero/Mybeisaierwavetest.git

    虽然简单,但是推荐还是得自己动手做一做。

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台