1,我们上一篇介绍了贝塞尔曲线推到原理和在Android里的简单使用,今天就和来写写贝塞尔曲线的实际应用,今天实现的效果图如下:

2,思路分析

  我们知道首先我们的view是一个圆,这里的圆其实是由四块三阶贝塞尔曲线组成的,左上、右上、左下、右下这四块贝塞尔曲线组成,那么让我们来开始吧

  • 准备阶段

  创建一个类MyViewCircle继承自View,重写构造方法,重写onDraw()方法

public class MyViewCircle extends View {
public MyViewCircle(Context context) {
this(context, null);
} public MyViewCircle(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
} public MyViewCircle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) { }
}
  • 绘制X、Y轴

  由于我们的view是展示在屏幕的正中央的,为了我们以后标识点方便,这里我们以屏幕的正中心为(0,0)坐标绘制出来

  在onSizeChange()方法中获得mCenterX、mCenterY的坐标,绘制X,Y轴,代码如下:

 private int mCenterX;
private int mCenterY;
private Paint mPaint;
//省略代码.........................
//初始化画笔
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true); @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); //初始化坐标系
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2;
} @Override
protected void onDraw(Canvas canvas) {
canvas.save();
//绘制x,y轴坐标系
canvas.drawLine(mCenterX, 0, mCenterX, getHeight(), mPaint);
canvas.drawLine(0, mCenterY, getWidth(), mCenterY, mPaint); canvas.restore();
}

 运行效果如下:

 

  • 从三阶贝塞尔曲线得到半圆的效果

  我们上一篇简单的介绍了下三阶贝塞尔,当我们的两个控制点距离数据点为一下坐标时我们绘制出来的曲线是类似于四分之一圆弧的,效果图如下:

  关于怎么计算出这两个控制点相对于数据点的相对坐标的,这是一个难点,不过还好,在stackoverflow上有人计算出来了,这是链接,我们可以得出一个常量0.552284749831,即相对于原点坐标,半径是mCircleRadius我们的坐标的0.55228倍,这里我为了方便,直接取了0.5,所以这就解释了为什么我们效果图中的圆有点瘪(保持微笑)

  • 绘制数据点

  这里我们四个数据点分别是的是半径为mCircleRadius,圆心坐标为(mCenterX,mCenterY)的圆与我们X、Y轴的焦点,绘制代码如下:

    private Paint mPaintCircle;
private Paint mPaintPoint;
private int mCircleRadius;
private List<PointF> mPointDatas; //放置四个数据点的集合
private List<PointF> mPointControlls;//方式8个控制点的集合 //省略代码.....
//初始化数据
mPaintCircle = new Paint();
mPaintCircle.setColor(Color.RED);
mPaintCircle.setStrokeWidth(10);
mPaintCircle.setStyle(Paint.Style.STROKE);
mPaintCircle.setAntiAlias(true); mPaintPoint = new Paint();
mPaintPoint.setColor(Color.BLACK);
mPaintPoint.setStrokeWidth(5);
mPaintPoint.setStyle(Paint.Style.FILL);
mPaintPoint.setAntiAlias(true); mCircleRadius = 150; //在onSizeChange方法中初始化四个数据点
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); //初始化坐标系
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2; mPointDatas = new ArrayList<>();
mPointControlls = new ArrayList<>(); mPointDatas.add(new PointF(mCenterX, mCenterY - mCircleRadius));
mPointDatas.add(new PointF(mCenterX + mCircleRadius, mCenterY));
mPointDatas.add(new PointF(mCenterX, mCenterY + mCircleRadius));
mPointDatas.add(new PointF(mCenterX - mCircleRadius, mCenterY));
}
//在onDraw方法中绘制数据点
@Override
protected void onDraw(Canvas canvas) {
//绘制x,y轴坐标系
canvas.drawLine(mCenterX, 0, mCenterX, getHeight(), mPaint);
canvas.drawLine(0, mCenterY, getWidth(), mCenterY, mPaint);
//绘制数据点
for (int i = 0; i < mPointDatas.size(); i++) {
canvas.drawPoint(mPointDatas.get(i).x, mPointDatas.get(i).y, mPaintPoint);
}
}

  效果图如下:

  • 绘制控制点

  由我们上面的的到的常量0.552284749831,这里使用的是0.5,所以,代码如下

 @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); //初始化坐标系
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2; mPointDatas = new ArrayList<>();
mPointControlls = new ArrayList<>(); mPointDatas.add(new PointF(mCenterX, mCenterY - mCircleRadius));
mPointDatas.add(new PointF(mCenterX + mCircleRadius, mCenterY));
mPointDatas.add(new PointF(mCenterX, mCenterY + mCircleRadius));
mPointDatas.add(new PointF(mCenterX - mCircleRadius, mCenterY)); mPointControlls.add(new PointF(mCenterX + mCircleRadius / 2, mCenterY - mCircleRadius));
mPointControlls.add(new PointF(mCenterX + mCircleRadius, mCenterY - mCircleRadius / 2)); mPointControlls.add(new PointF(mCenterX + mCircleRadius, mCenterY + mCircleRadius / 2));
mPointControlls.add(new PointF(mCenterX + mCircleRadius / 2, mCenterY + mCircleRadius)); mPointControlls.add(new PointF(mCenterX - mCircleRadius / 2, mCenterY + mCircleRadius));
mPointControlls.add(new PointF(mCenterX - mCircleRadius, mCenterY + mCircleRadius / 2)); mPointControlls.add(new PointF(mCenterX - mCircleRadius, mCenterY - mCircleRadius / 2));
mPointControlls.add(new PointF(mCenterX - mCircleRadius / 2, mCenterY - mCircleRadius));
} //在onDraw方法中添加控制点的绘制
@Override
protected void onDraw(Canvas canvas) {
//绘制x,y轴坐标系
canvas.drawLine(mCenterX, 0, mCenterX, getHeight(), mPaint);
canvas.drawLine(0, mCenterY, getWidth(), mCenterY, mPaint);
//绘制数据点
canvas.save();
for (int i = 0; i < mPointDatas.size(); i++) {
canvas.drawPoint(mPointDatas.get(i).x, mPointDatas.get(i).y, mPaintPoint);
}
//绘制控制点
for (int i = 0; i < mPointControlls.size(); i++) {
canvas.drawPoint(mPointControlls.get(i).x, mPointControlls.get(i).y, mPaintPoint);
}
}

  绘制后效果图如下:

  • 绘制三阶贝塞尔曲线

  先来绘制右上角四分之一圆弧的贝塞尔曲线看看效果,在onDraw中调用如下代码:

    //利用三阶贝塞尔曲线实现画圆
Path path = new Path();
path.moveTo(mPointDatas.get(0).x, mPointDatas.get(0).y);
path.cubicTo(mPointControlls.get(0).x, mPointControlls.get(0).y, mPointControlls.get(1).x, mPointControlls.get(1).y, mPointDatas.get(1)x, mPointDatas.get(1).y); //绘制
canvas.drawPath(path, mPaintCircle);

  看一下效果:

  看到了吧  ,效果可以吧,然后我们继续来把后面三条弧线绘制玩,代码如下:

//利用三阶贝塞尔曲线实现画圆
Path path = new Path();
path.moveTo(mPointDatas.get(0).x, mPointDatas.get(0).y);
for (int i = 0; i < mPointDatas.size(); i++) {
if (i == mPointDatas.size() - 1) {
path.cubicTo(mPointControlls.get(2 * i).x, mPointControlls.get(2 * i).y, mPointControlls.get(2 * i + 1).x, mPointControlls.get(2 * i + 1).y, mPointDatas.get(0).x, mPointDatas.get(0).y); } else {
path.cubicTo(mPointControlls.get(2 * i).x, mPointControlls.get(2 * i).y, mPointControlls.get(2 * i + 1).x, mPointControlls.get(2 * i + 1).y, mPointDatas.get(i + 1).x, mPointDatas.get(i + 1).y);
} }
canvas.drawPath(path, mPaintCircle);

  效果如下:

  • 改变数据点和控制点,重绘制view

  这一步最关键,先来看看我们下面的动画效果

   可以看到我们上面动画实现的效果移动有五个点变了:一个Y正轴上数据点改变,四个Y负轴控制点改变了,ok,知道了这些了我们基本上知道了怎么实现了,代码如下:

    private int mDuration = 1000; //动画总时间
private int mCurrTime = 0; //当前已进行时间
private int mCount = 100;//将总时间划分多少块
private float mPiece = mDuration / mCount; //每一块的时间 ; //在onDraw()方法中动态的刷新view,有人肯定会问,120,80之类的怎么的出来的,我只能说调出来的好嘛(手动微笑),代码如下:
//动态改变数据点和辅助点
mCurrTime += mPiece;
if (mCurrTime < mDuration) {
mPointDatas.get(0).y += 120 / mCount;
mPointControlls.get(2).x -= 20.0 / mCount; mPointControlls.get(3).y -= 80.0 / mCount;
mPointControlls.get(4).y -= 80.0 / mCount;
mPointControlls.get(5).x += 20.0 / mCount; postInvalidateDelayed((long) mPiece);
}

  ok,这样我们基本上全部完成了,看一下效果

  ok,这样我们就实现这个效果了,有没有很简单,有需要源码的同学可以在我的Github下载,明天继续,See You Next Time!!!

Android -- 贝塞尔使圆渐变为桃心的更多相关文章

  1. Android ActionBar标题和渐变背景

    需要在AndroidManifest.xml中设置 android:theme="@style/Theme.AppCompat" 如果提示找不到,请按下图设置: 至于如何引入的方法 ...

  2. 在Android系统中修改Android.mk使其同时编译rgb2565和rgb2888(向out/host/linux-x86/bin/下新增加一个工具命令)【转】

    本文转载自:http://blog.csdn.net/mu0206mu/article/details/7514559 在Android系统中修改android.mk使其同时编译rgb2565和rgb ...

  3. Android 贝塞尔曲线解析

    相信很多同学都知道"贝塞尔曲线"这个词,我们在很多地方都能经常看到.利用"贝塞尔曲线"可以做出很多好看的UI效果,本篇博客就让我们一起学习"贝塞尔曲线 ...

  4. android自定义进度圆与定时任务

    先看代码:自定进度圆 public class ProgressCircle extends View { private Paint paint; private int strokewidth = ...

  5. Android 贝塞尔曲线 折线图

    1.贝塞尔曲线:http://baike.baidu.com/view/60154.htm,在这里理解什么是贝塞尔曲线 2.直接上图: 3.100多行代码就可以画出贝塞尔曲线,直接上代码 packag ...

  6. Android Shape画圆,矩形

    画圆环代码如下: 画圆环,外边的边界宽度大一点即可: <?xml version="1.0" encoding="utf-8"?> <shap ...

  7. Android -- 贝塞尔曲线公式的推导

    1,最近看了几个不错的自定义view,发现里面都会涉及到贝塞尔曲线知识,深刻的了解到贝塞尔曲线是进阶自定义view的一座大山,so,今天先和大家来了解了解. 2,贝塞尔曲线作用十分广泛,简单举几个的栗 ...

  8. Android -- 贝塞尔二阶实现饿了么加入购物车效果

    1,上周我们实现了简单的三阶贝塞尔曲线效果实例,今天是使用二阶贝塞尔曲线加动画实现的加入购物车效果,在码代码过程中出现了些问题,过一下和大家来探讨探讨,先看一下效果图 2,从上面的效果来看我们基本上可 ...

  9. Android -- 贝塞尔实现水波纹动画(划重点!!)

    1,昨天看到了一个挺好的ui效果,是使用贝塞尔曲线实现的,就和大家来分享分享,还有,在写博客的时候我经常会把自己在做某种效果时的一些问题给写出来,而不是像很多文章直接就给出了解决方法,这里给大家解释一 ...

随机推荐

  1. matab plot指令和低通滤波器的响应图

    一.plot额外的四个属性模板使用 代码 % 提示 disp ('该功能练习plot额外四个属性功能'); %初始化快捷式数组 figure(); x=:pi/:*pi; y=exp(*sin(x)) ...

  2. bootstrap 预定义样式风格

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. asp.net core mvc实现伪静态功能

    在大型网站系统中,为了提高系统访问性能,往往会把一些不经常变得内容发布成静态页,比如商城的产品详情页,新闻详情页,这些信息一旦发布后,变化的频率不会很高,如果还采用动态输出的方式进行处理的话,肯定会给 ...

  4. Entity Framework Code First约定

    Code First使你能够通过C# 或者 Visual Basic .NET来描述模型,模型的基本规则通过使用约定来进行检查,而约定就是一系列内置的规则. 在Code First中基于类的定义通过一 ...

  5. 探索Javascript设计模式---单例模式

    最近打算系统的学习javascript设计模式,以便自己在开发中遇到问题可以按照设计模式提供的思路进行封装,这样可以提高开发效率并且可以预先规避很多未知的问题. 先从最基本的单例模式开始. 什么是单例 ...

  6. linux文件和目录权限

    linux系统文件和目录的权限说明 文件权限是Linux系统的第一道安全防线,基本的权限有读取(r).写入(w)和执行(x): 文件访问模式 读取:用户能够读取文件信息,查看文件内容. 写入:用户可以 ...

  7. css4激动人心的新特性及浏览器支持度

    CSS3的选择器提供了很多像:nth-child这样有用的选择器,并且得到浏览器支持.CSS的第四代 选择器CSS4选择器),经我们带来了更多有用的选择器. 1.否定伪类:not 否定伪类选择器其实在 ...

  8. hibernate系列笔记(2)---Hibernate的核心API

    Hibernate的核心API 一般我们通过hibernate进行操作的时候,都会遵循下面的流程,那么接下来我对每一个步骤进行讲解: 1 public void testInsert() { 2 // ...

  9. 规范 : login 对象的account

    accountInfo对象是前台pass 给后台,基本上这对象的资源有可能不是完整的. 举个例子:register 需要accountInfo 对象(内容数量是依据项目),但是在login时,不需要给 ...

  10. 用css实现3D立方体旋转特效

    先来看运行后出来的效果 它是在不停运行的一个立方体 先来看html部分的代码 <div class="rect-wrap"> <!--舞台元素,设置perspec ...