版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/qq_37717853/article/details/86483838
先上效果:
gif可能看不到,上两张截图
贝塞尔曲线知识的简单应用,自定义了个圆形控件,控件内水波纹自动上升,通过几个简单的seekbar做了个简单的颜色选择器。
很早之前就听过说贝塞尔曲线,只是一直没时间好好学习一番,最近忙里偷闲,便想着了解一下,网上关于贝塞尔曲线的解释很多,这里我就暂时不班门弄斧了,如果还不甚了解又有兴趣了解的小伙伴,可以去看看这个作者的博客,其中关于贝塞尔曲线的介绍那篇写的还挺通俗易懂的。
部分关键代码如下:
1.为了控件更灵活易于拓展,自定义一些属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BezierView">
<!--水位上升动画的执行时间-->
<attr name="duration1" format="integer"/>
<!--水波周期移动动画的执行时间-->
<attr name="duration2" format="integer"/>
<!--水波高度-->
<attr name="waveHeight" format="integer"/>
<!--水波长度-->
<attr name="waveWidth" format="integer"/>
<!--初始点Y坐标,即水位初始位置-->
<attr name="originY" format="integer"/>
<!--背景圆的颜色-->
<attr name="cirColor" format="color"/>
<!--背景圆的轮廓的颜色-->
<attr name="cirSideColor" format="color"/>
</declare-styleable>
2.创建自定义view,onDraw中的代码如下:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画背景圆
mCanvas.drawCircle(radius,radius,radius,bgCirPaint);
//不断的计算波浪的路径
calculatePath();
//在背景圆上绘制水波
mCanvas.drawPath(path,paint);
//将画好的圆绘制到画布上
canvas.drawBitmap(mbitmap,0,0,null);
//绘制圆的轮廓(边)
canvas.drawCircle(radius,radius,radius,cirPaint);
}
3.计算波浪的路径path,这里便用到了贝塞尔曲线来绘制水波纹的路径:
private void calculatePath() {
//初始点
path.moveTo(-waveWidth + dex, originY - dey);
for (int i = -waveWidth; i < width + waveWidth; i += waveWidth) {
//二阶贝塞尔曲线绘制
path.rQuadTo(waveWidth / 4, -waveHeight, waveWidth / 2, 0);
path.rQuadTo(waveWidth / 4, waveHeight, waveWidth / 2, 0);
}
//绘制连线
path.lineTo(width, height);
path.lineTo(0, height);
path.close();//关闭当前轮廓。如果当前点不等于轮廓的第一个点,则自动添加线段,使其形成闭区间。
}
4.动画效果部分:水波上升和水波移动效果,都可以通过属性动画来实现,而且实现方式几乎一样,这里是在自定义view内实现一个public的开启动画的方法供activity在适当地时候去调用:
public void startAnimation() {
//初始化水波效果动画
animator1 = ValueAnimator.ofFloat(0, 1);//定义一个值从0到1的控制值
animator1.setDuration(duration2);//水波移动周期时长
animator1.setRepeatCount(ValueAnimator.INFINITE);//设置循环次数,0为1次,无穷尽的一直执行:ValueAnimator.INFINITE
animator1.setInterpolator(new LinearInterpolator());//插值器,线性匀速播放动画,不设置其实也是默认匀速
//监听
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//获取属性动画当前值(0-1)
float fraction = (float) animator1.getAnimatedValue();
dex = (int) (waveWidth * fraction);//根据动画进行进度,动态修改整条波浪线的长度
//清空画布
if (path != null)
path.reset();
//刷新
postInvalidate();
}
});
//启动动画
animator1.start();
//初始化水位上升动画
animator2 = ValueAnimator.ofFloat(0, 1);//定义一个值从0到1的控制值
animator2.setDuration(duration1);//水波上升周期时长
animator2.setRepeatCount(0);//设置循环次数,0为1次,无穷尽的一直执行:ValueAnimator.INFINITE
animator2.setInterpolator(new LinearInterpolator());//插值器,线性匀速播放动画,不设置其实也是默认匀速
//监听
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//获取属性动画当前值(0-1)
float fraction = (float) animator2.getAnimatedValue();
dey = (int) ((height+waveHeight*2) * fraction);//根据动画进度,dey的值会从0慢慢接近于heitht+waveHeight*2的高度
//清空画布
if (path != null)
path.reset();
//刷新
postInvalidate();
}
});
//启动动画
animator2.start();
}
关键代码就是上面的这些啦,估计有经验的小伙伴看到这里已经知道怎么做了,要是懒得自己写,可以直接去下我的demo也行,demo中已有详细注释:
Demo下载地址:https://download.csdn.net/download/qq_37717853/10916906
项目参考博客:
1.https://www.cnblogs.com/wjtaigwh/p/6750184.html
2.https://blog.csdn.net/u013087553/article/details/68490170