欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-04-24 18:15:01
这一次要绘制出波浪效果,也是小白的我第一次还望轻喷。首先当然是展示效果图啦: 一.首先来说说实现思路。 想到波浪效果,当然我第一反应是用正余弦波来设计啦(也能通过贝塞尔曲线,这里我不提及这个方法但是在demo里这种方法也实现了),肯定要绘制一个静态的波,然后通过不断的对它平移刷新,这样最简单的波浪效 ......

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

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

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

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

    二.绘制。

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

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

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

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         }

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

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

 

   画出静态的波形图之后,我们只要每次让这个波向前或者向后移动一定周期再不断刷新,就能出现波浪效果了。重写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对波浪波峰波谷与水位变化的调控就能达到上面的效果。下面是完整的代码:

 

Android自定义控件练手——波浪效果
  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             @Override
133             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             @Override
145             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

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