Android绘制波浪线

编程入门 行业动态 更新时间:2024-10-22 19:47:26

Android绘制<a href=https://www.elefans.com/category/jswz/34/1769735.html style=波浪线"/>

Android绘制波浪线

今天让我们来绘制一条波浪线,并且提供了控制波浪大小和波浪速度的方法,首先我们来看看完成的效果:

效果是不是还不错,接下来我们就来看看具体怎么实现的吧!

如果你学会了这种绘制波浪线的方式,可以了解下波浪线的进阶画法:
Android绘制波浪线 进阶


实现思路

波浪线的绘制
波浪线是怎么实现的呢,其实这用到的内塞尔曲线。
大家可以在这里看看贝塞尔曲线的使用方式。——贝塞尔曲线开发的艺术

使用一阶贝塞尔曲线画出前半截凸起的曲线,再使用一阶贝塞尔曲线画出后半截的贝塞尔曲线。
具体实现方式,为了看着简单点,我就直接写数字了。

    @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint paint = new Paint();paint.setColor(Color.RED);paint.setAntiAlias(true);paint.setStrokeWidth(5);paint.setStyle(Paint.Style.STROKE);Path path = new Path();path.moveTo(0, 500);path.quadTo(screenWidth / 4, 300, screenWidth / 2, 500);path.moveTo(screenWidth / 2, 500);path.quadTo(screenWidth / 4 * 3, 700, screenWidth, 500);canvas.drawPath(path, paint);}

效果大概是这个样子:

波浪的动态
大家有没有觉得这个波浪线很像y=sinx这个函数图像,只要y=sin(x+a),a是一个随着时间无限增大的值,该函数图像就可以动起来,大家可以在这个网站上试试——Desmos图形计算器
大概要输入一个这样的函数:y=sin(x/5+a),a做为一个滑块。这样大家就可以看到波浪线的动态图了。

好了,点子已经有了,只要我们把那个波浪线向后移动,就可以看出波浪的动态效果,于是我们做出了如下代码:

    private int screenWidth;private float xoffset = 0;public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);screenWidth = wm.getDefaultDisplay().getWidth();ValueAnimator animator = new ValueAnimator();animator.setFloatValues(0, screenWidth);animator.setDuration(3000);animator.setRepeatCount(-1);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {xoffset = (float) animation.getAnimatedValue();invalidate();}});animator.start();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint paint = new Paint();paint.setColor(Color.RED);paint.setAntiAlias(true);paint.setStrokeWidth(5);paint.setStyle(Paint.Style.STROKE);Path path = new Path();path.moveTo(0 + xoffset, 500);path.quadTo(screenWidth / 4 + xoffset, 300, screenWidth / 2 + xoffset, 500);path.moveTo(screenWidth / 2 + xoffset, 500);path.quadTo(screenWidth / 4 * 3 + xoffset, 700, screenWidth + xoffset, 500);canvas.drawPath(path, paint);}

效果如下:

。。。

好吧,确实,这确实没有想象中的波浪效果,而且还有点——操蛋。。
我也没有说这是最终效果啊,我只是说明了一下动态效果,那么要怎么才能让波浪动的毫无破绽呢,其实我们可以。。。

在屏幕外面再画一个波浪,这样动起来就没有破绽了。
于是有了以下代码:

    private int screenWidth;private float xoffset = 0;private Paint paint;private Path path;public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);screenWidth = wm.getDefaultDisplay().getWidth();paint = new Paint();paint.setColor(Color.RED);paint.setAntiAlias(true);paint.setStrokeWidth(5);paint.setStyle(Paint.Style.STROKE);path = new Path();ValueAnimator animator = new ValueAnimator();animator.setFloatValues(0, screenWidth);animator.setDuration(3000);animator.setInterpolator(new LinearInterpolator());animator.setRepeatCount(-1);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {xoffset = (float) animation.getAnimatedValue();invalidate();}});animator.start();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);path.reset();path.moveTo(0 + xoffset, 500);path.quadTo(screenWidth / 4 + xoffset, 300, screenWidth / 2 + xoffset, 500);path.moveTo(screenWidth / 2 + xoffset, 500);path.quadTo(screenWidth / 4 * 3 + xoffset, 700, screenWidth + xoffset, 500);path.moveTo(0 + xoffset - screenWidth, 500);path.quadTo(screenWidth / 4 + xoffset - screenWidth, 300, screenWidth / 2 + xoffset - screenWidth, 500);path.moveTo(screenWidth / 2 + xoffset - screenWidth, 500);path.quadTo(screenWidth / 4 * 3 + xoffset - screenWidth, 700, screenWidth + xoffset - screenWidth, 500);canvas.drawPath(path, paint);}

我们再来看看效果图:

哎呦,不错喔,是不是有点意思了,接下来我们只要做点细节上的处理,按照一个正常的自定义view的流程走,我们最终会得到如下代码:

public class MyWaveView extends View {private Paint mPaint;private Path mPath;// view宽度private int width;// view高度private int height;// 波浪高低偏移量private int offset = 20;// X轴,view的偏移量private int xoffset = 0;// view的Y轴高度private int viewY = 0;// 波浪速度private int waveSpeed = 50;private ValueAnimator animator;public MyWaveView(Context context) {super(context);init(context);}public MyWaveView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(context);}private void init(Context context) {mPaint = new Paint();mPaint.setColor(Color.RED);mPaint.setStyle(Paint.Style.STROKE);mPaint.setAntiAlias(true);mPaint.setStrokeWidth(5);mPath = new Path();WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);width = wm.getDefaultDisplay().getWidth();animator = new ValueAnimator();animator.setFloatValues(0, width);animator.setDuration(waveSpeed * 20);animator.setRepeatCount(-1);animator.setInterpolator(new LinearInterpolator());animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float change = (float) animation.getAnimatedValue();xoffset = (int) change;invalidate();}});animator.start();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));}private int measureWidth(int measureSpec) {int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);//设置一个默认值,就是这个View的默认宽度为500,这个看我们自定义View的要求int result = 500;if (specMode == MeasureSpec.AT_MOST) {//相当于我们设置为wrap_contentresult = specSize;} else if (specMode == MeasureSpec.EXACTLY) {//相当于我们设置为match_parent或者为一个具体的值result = specSize;}width = result;return result;}private int measureHeight(int measureSpec) {int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);int result = 500;if (specMode == MeasureSpec.AT_MOST) {result = specSize;} else if (specMode == MeasureSpec.EXACTLY) {result = specSize;}height = specSize;return result;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mPath.reset();viewY = height / 2;// 绘制屏幕内的波浪mPath.moveTo(xoffset, viewY);mPath.quadTo(width / 4 + xoffset, viewY - offset, width / 2 + xoffset, viewY);mPath.moveTo(width / 2 + xoffset, viewY);mPath.quadTo(width / 4 * 3 + xoffset, viewY + offset, width + xoffset, viewY);// 绘制屏幕外的波浪mPath.moveTo(xoffset - width, viewY);mPath.quadTo(width / 4 + xoffset - width, viewY - offset, width / 2 + xoffset - width, viewY);mPath.moveTo(width / 2 + xoffset - width, viewY);mPath.quadTo(width / 4 * 3 + xoffset - width, viewY + offset, width + xoffset - width, viewY);canvas.drawPath(mPath, mPaint);}/*** 设置 波浪的高度*/public void setWaveHeight(int waveHeight){offset = waveHeight;}/*** 获取 波浪的高度*/public int getWaveHeight(){return offset;}/*** 设置 波浪的速度*/public void setWaveSpeed(int speed){waveSpeed = 2000 - speed * 20;animator.setDuration(waveSpeed);}/*** 获取 波浪的速度*/public int getWaveSpeed(){return waveSpeed;}}

好了,大概实现一条波浪线的流程大概就是这样。

总结

使用贝塞尔曲线做出波浪线,使用属性动画让波浪线动起来
连续原理:
最开始是这样的:

移动后:

当移动到图二的时候,该动画已经结束了,但由于该动画指定了无限循环,所以实际上又会回到图一的形状。这样解释可能不太直观,所以:

好了,这下各位看官看的够清楚吧,里面的黑框就是我们的手机屏幕大小,这样给我们造成一种无限波动的假象,大家可以试试用两只手挡住黑框两边,能看到波动效果哦!

源码下载:

要不要了解下波浪线的进阶画法:Android绘制波浪线 进阶

更多推荐

Android绘制波浪线

本文发布于:2023-06-20 23:53:33,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/808834.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:波浪   Android

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!