android中贝塞尔曲线的应用示例
发布时间 - 2026-01-11 00:10:57 点击率:次前言:

贝塞尔曲线又称贝兹曲线,它的主要意义在于无论是直线或曲线都能在数学上予以描述。最初由保罗·德卡斯特里奥(Paul de Casteljau)于1959年运用德卡斯特里奥演算法开发(de Casteljau Algorithm),在1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表。目前广泛应用于图形绘制领域来模拟光滑曲线,为计算机矢量图形学奠定了基础。在一些图形处理软件中都能见到贝塞尔曲线,比如CorelDraw中翻译成“贝赛尔工具”;而在Fireworks中叫“画笔”;Photoshop中叫“钢笔工具”。下图为Photoshop中用钢笔绘制的贝塞尔曲线,共绘制了三条贝塞尔曲线:
数学表达
术语:数据点、控制线、控制点、德卡斯特里奥算法、一阶,二阶,三阶,n阶……
- 数据点:一条贝塞尔曲线的起始点和终结点都叫数据点。
- 控制线:在图中可以看到那条中心点为数据点的线段,每个数据点对应一条控制线
- 控制点:就是控制线的端点,通过控制线随着控制点的变化而变化;数据点和控制点决定一条贝塞尔曲线。
- 一阶贝塞尔曲线:其实是一条直线段,没有控制点。
一阶贝塞尔曲线示意图
一阶贝塞尔曲线公式
二阶贝塞尔曲线:图中第二段为二阶贝塞尔曲线,只有一个控制点,即只有一个控制点和两个数据点来决定曲线形状。
二阶贝塞尔曲线公式
二阶公式推导:
二阶贝塞尔曲线t=0.6示意图.gif
根据控制点和数据点,对贝塞尔曲线进行约束,满足的条件为
问题变为:已知P0(x0,y0), P1(x1,y1), P2(x2,y2),根据上式求P点坐标?
先求出A、B点坐标,其坐标
PA=P0+(P1-P0)·t
PB=P1+(P2-P1)·t
之后求P点坐标,其坐标
P=PA+(PB-PA)·t
P=(1-t)2P0+2t(1-t)P1+t2P2, t∈[0,1]
三阶贝塞尔曲线:图中第三段为三阶贝塞尔曲线,有两个控制点和两个数据点决定的曲线,同样满足等比条件:
三阶贝塞尔曲线
三阶贝塞尔曲线公式
德卡斯特里奥算法的思想:给定数据点和控制点P0、P1…Pn,首先将数据点和控制点连接形成一条折线,计算出每条折线上面的一点,使得初始数据点(初始控制点)到该点的距离与初始数据点(初始控制点)到终止数据点(终止控制点)的距离之比为t:1。将这些点连接起来形成新的折线(折线少了一段),用递归的算法继续计算,指导只有两个点,在这两个点形成的线段上去一点,满足以上的比例关系。随着t的从0到1的变化,该点的集合形成了贝塞尔曲线。
n阶贝塞尔曲线公式如下。
Android中的应用
对android中如何获取贝塞尔曲线上的点,如何绘制贝塞尔曲线,以及结合贝塞尔曲线的知识做出的效果进行分析。
1. 获取贝塞尔曲线上点的坐标
在android中并没有直接获取的方法,因此需要利用上面的公式进行计算,以二阶贝塞尔曲线为例,获取51个点,主要是t从0开始到1间取51项的等差数列:
public class MainActivity extends AppCompatActivity {
private static final float mPointNum = 50f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
PointF mStartPoint = new PointF(0, 0);
PointF mEndPoint = new PointF(0, 1200);
PointF mControlPoint = new PointF(500, 600);
List<PointF> mPointList = new ArrayList<>();
for (int i = 0; i <= mPointNum; i++) {
mPointList.add(getBezierPoint(mStartPoint, mEndPoint, mControlPoint, i / mPointNum));
Log.d("Bezier", "X:" + mPointList.get(i).x + " Y:" + mPointList.get(i).y);
}
}
private PointF getBezierPoint(PointF start, PointF end, PointF control, float t) {
PointF bezierPoint = new PointF();
bezierPoint.x = (1 - t) * (1 - t) * start.x + 2 * t * (1 - t) * control.x + t * t * end.x;
bezierPoint.y = (1 - t) * (1 - t) * start.y + 2 * t * (1 - t) * control.y + t * t * end.y;
return bezierPoint;
}
}
如果需要更高阶,可以使用递归函数来运算
//用递归获取贝塞尔曲线点的x轴坐标
private float getBezierPointX(int n, int position, float t) {
if (n == 1) {
return (1 - t) * mPointList.get(position).x + t * mPointList.get(position + 1).x;
}
return (1 - t) * getBezierPointX(n - 1, position, t) + t * getBezierPointX(n - 1, position + 1, t);
}
//用递归获取贝塞尔曲线点的x轴坐标
private float getBezierPointX(int n, int position, float t) {
if (n == 1) {
return (1 - t) * mPointList.get(position).x + t * mPointList.get(position + 1).x;
}
return (1 - t) * getBezierPointX(n - 1, position, t) + t * getBezierPointX(n - 1, position + 1, t);
}
private ArrayList<PointF> buildBezierPoints() {
ArrayList<PointF> points = new ArrayList<>();
int order = mPointList.size() - 1;
float delta = 1.0f / POINT_NUM;
for (float t = 0; t <= 1; t += delta) {
// Bezier点集
points.add(new PointF(getBezierPointX(order, 0, t), getBezierPointY(order, 0, t)));
}
return points;
}
2. 绘制贝塞尔曲线
Android 中的Path类可以直接绘制一阶到三阶的贝塞尔曲线,在onDraw(Canvas canvas) 方法中使用:
绘制一阶贝塞尔曲线:
canvas.drawLine(start.x,start.y,end.x,end.y);
绘制二阶贝塞尔曲线:
mPath.moveTo(startPoint.x, startPoint.y);//起点 mPath.quadTo(controlPoint1.x, controlPoint1.y, endPoint.x, endPoint.y); canvas.drawPath(mPath, mPaint);
绘制三阶贝塞尔曲线:
mPath.moveTo(startPoint.x, startPoint.y);//起点 mPath.cubicTo(controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y); canvas.drawPath(mPath, mPaint);
绘制n阶贝塞尔曲线
n阶贝塞尔曲线绘制,需要结合递归函数,设定一条曲线由多少个点组成,通过循环获取每个点的坐标进行绘制。
7阶贝塞尔曲线
8阶贝塞尔曲线
3.Demo
贝塞尔曲线在android中最常用的是做出一些动画特效,如QQ消息数拖拽形变效果,一些炫酷的下拉刷新控件,阅读软件的翻书效果,一些平滑的折线图的制作,某图片的运动轨迹等……
3.1 形状变形
只需要知道变形前和变形后图形的数据点和控制点即可,通过改变数据点和控制点使得形状发生改变。
初始化
private float[] mData = new float[8]; // 顺时针记录绘制圆形的四个数据点 private float[] mCtrl = new float[16]; // 顺时针记录绘制圆形的八个控制点 private float mDuration = 1000; // 变化总时长 private float mCurrent = 0; // 当前已进行时长 private float mCount = 100; // 将时长总共划分多少份 private float mPiece = mDuration / mCount; // 每一份的时长
在onDraw(Canvas canvas)方法中绘制
path.reset();
path.moveTo(mData[0], mData[1]);
path.cubicTo(mCtrl[0], mCtrl[1], mCtrl[2], mCtrl[3], mData[2], mData[3]);
path.cubicTo(mCtrl[4], mCtrl[5], mCtrl[6], mCtrl[7], mData[4], mData[5]);
path.cubicTo(mCtrl[8], mCtrl[9], mCtrl[10], mCtrl[11], mData[6], mData[7]);
path.cubicTo(mCtrl[12], mCtrl[13], mCtrl[14], mCtrl[15], mData[0], mData[1]);
canvas.drawPath(path, mPaint);
mCurrent += mPiece;
if (mCurrent < mDuration) {
mData[1] -= 120 / mCount;
mCtrl[7] += 80 / mCount;
mCtrl[9] += 80 / mCount;
mCtrl[4] -= 20 / mCount;
mCtrl[10] += 20 / mCount;
postInvalidateDelayed((long) mPiece);
}
3.2 漂浮的爱心
爱心的漂浮轨迹就是一条三阶贝塞尔曲线,结合属性动画中的估值器进行设置。
首先定义一个属性动画的估值器
public class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF mControlP1;
private PointF mControlP2;
public BezierEvaluator(PointF controlP1, PointF controlP2) {
this.mControlP1 = controlP1;
this.mControlP2 = controlP2;
}
@Override
public PointF evaluate(float time, PointF start, PointF end) {
float timeLeft = 1.0f - time;
PointF point = new PointF();
point.x = timeLeft * timeLeft * timeLeft * (start.x) + 3 * timeLeft * timeLeft * time *
(mControlP1.x) + 3 * timeLeft * time *
time * (mControlP2.x) + time * time * time * (end.x);
point.y = timeLeft * timeLeft * timeLeft * (start.y) + 3 * timeLeft * timeLeft * time *
(mControlP1.y) + 3 * timeLeft * time *
time * (mControlP2.y) + time * time * time * (end.y);
return point;
}
}
之后自定义一个view可以生成爱心,添加透明度,缩放等动画和根据贝塞尔曲线改变其位置的属性动画。
初始化爱心图片和多个插值器等,到时随即选取
private void init() {
// 初始化显示的图片
drawables = new Drawable[3];
drawables[0] = getResources().getDrawable(R.drawable.red);
drawables[1] = getResources().getDrawable(R.drawable.yellow);
drawables[2] = getResources().getDrawable(R.drawable.green);
// 初始化插补器
mInterpolators = new Interpolator[4];
mInterpolators[0] = new LinearInterpolator();// 线性
mInterpolators[1] = new AccelerateInterpolator();// 加速
mInterpolators[2] = new DecelerateInterpolator();// 减速
mInterpolators[3] = new AccelerateDecelerateInterpolator();// 先加速后减速
// 底部 并且 水平居中
dWidth = drawables[0].getIntrinsicWidth();
dHeight = drawables[0].getIntrinsicHeight();
lp = new LayoutParams(dWidth, dHeight);
lp.addRule(CENTER_HORIZONTAL, TRUE);// 这里的TRUE 要注意 不是true
lp.addRule(ALIGN_PARENT_BOTTOM, TRUE);
}
入场动画
private AnimatorSet getEnterAnimator(final View target) {
ObjectAnimator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, 0.2f, 1f);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, 0.2f, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, 0.2f, 1f);
AnimatorSet enter = new AnimatorSet();
enter.setTarget(target);
enter.setInterpolator(new LinearInterpolator());
enter.setDuration(500).playTogether(alpha, scaleX, scaleY);
return enter;
}
贝塞尔曲线动画
private ValueAnimator getBezierValueAnimator(final View target) {
// 初始化贝塞尔估值器
BezierEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));
// 起点在底部中心位置,终点在底部随机一个位置
ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) /
2, mHeight - dHeight), new PointF(random.nextInt(getWidth()), 0));
animator.setTarget(target);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 这里获取到贝塞尔曲线计算出来的的x y值 赋值给view 这样就能让爱心随着曲线走啦
PointF pointF = (PointF) valueAnimator.getAnimatedValue();
target.setX(pointF.x);
target.setY(pointF.y);
// alpha动画
target.setAlpha(1 - valueAnimator.getAnimatedFraction());
}
});
animator.setDuration(3000);
return animator;
}
结合动画添加爱心
public void addHeart() {
final ImageView imageView = new ImageView(getContext());
// 随机选一个爱心
imageView.setImageDrawable(drawables[random.nextInt(3)]);
imageView.setLayoutParams(lp);
addView(imageView);
AnimatorSet finalSet = new AnimatorSet();
AnimatorSet enterAnimatorSet = getEnterAnimator(imageView);//入场动画
ValueAnimator bezierValueAnimator = getBezierValueAnimator(imageView);//贝塞尔曲线路径动画
finalSet.playSequentially(enterAnimatorSet, bezierValueAnimator);
finalSet.setInterpolator(mInterpolators[random.nextInt(4)]);
finalSet.setTarget(imageView);
finalSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
removeView((imageView));//删除爱心
}
});
finalSet.start();
}
3.3 贝塞尔曲线侧边索引条
实例地址:FancyListIndexer_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# android
# 贝塞尔曲线
# android中贝塞尔曲线
# 画贝塞尔曲线
# Android把商品添加到购物车的动画效果(贝塞尔曲线)
# Android 利用三阶贝塞尔曲线绘制运动轨迹的示例
# android贝塞尔曲线实现波浪效果
# Android贝塞尔曲线初步学习第二课 仿QQ未读消息气泡拖拽黏连效果
# Android中贝塞尔曲线的绘制方法示例代码
# Android贝塞尔曲线实现填充不规则图形并随手指运动
# Android利用二阶贝塞尔曲线实现添加购物车动画详解
# Android贝塞尔曲线实现直播点赞效果
# Android Path绘制贝塞尔曲线实现QQ拖拽泡泡
# Android自定义View绘制贝塞尔曲线的方法
# 塞尔
# 递归
# 控制线
# 时长
# 里奥
# 卡斯特
# 图中
# 估值
# 只有一个
# 求出
# 的是
# 保罗
# 顺时针
# 等差数列
# 多个
# 在这
# 中心点
# 都能
# 只需
# 而在
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
Android使用GridView实现日历的简单功能
如何用景安虚拟主机手机版绑定域名建站?
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
如何构建满足综合性能需求的优质建站方案?
Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法
零基础网站服务器架设实战:轻量应用与域名解析配置指南
laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法
Python文件操作最佳实践_稳定性说明【指导】
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
香港服务器租用费用高吗?如何避免常见误区?
如何快速生成专业多端适配建站电话?
Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决
如何用AWS免费套餐快速搭建高效网站?
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
使用spring连接及操作mongodb3.0实例
如何基于PHP生成高效IDC网络公司建站源码?
laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法
Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤
网站制作免费,什么网站能看正片电影?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
实例解析Array和String方法
如何在IIS中配置站点IP、端口及主机头?
Windows家庭版如何开启组策略(gpedit.msc)?(安装方法)
Laravel如何配置Horizon来管理队列?(安装和使用)
香港服务器建站指南:免备案优势与SEO优化技巧全解析
Win11怎么开启自动HDR画质_Windows11显示设置HDR选项
如何在阿里云服务器自主搭建网站?
Laravel如何使用Sanctum进行API认证?(SPA实战)
高防服务器租用首荐平台,企业级优惠套餐快速部署
韩国服务器如何优化跨境访问实现高效连接?
如何在Windows 2008云服务器安全搭建网站?
Laravel怎么使用Intervention Image库处理图片上传和缩放
HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】
WordPress 子目录安装中正确处理脚本路径的完整指南
Laravel如何使用Blade模板引擎?(完整语法和示例)
Linux系统命令中screen命令详解
laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程
香港服务器部署网站为何提示未备案?
Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】
Linux安全能力提升路径_长期防护思维说明【指导】
想要更高端的建设网站,这些原则一定要坚持!
网站图片在线制作软件,怎么在图片上做链接?
Laravel定时任务怎么设置_Laravel Crontab调度器配置
如何快速重置建站主机并恢复默认配置?
Laravel API资源(Resource)怎么用_格式化Laravel API响应的最佳实践
深入理解Android中的xmlns:tools属性
Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道
如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)
Laravel怎么调用外部API_Laravel Http Client客户端使用

