自定义贝塞尔曲线

        有时候现有的控件无法满足我们的需求,打不到我们想要的效果,这时候就需要我们自己来写一个自定义view,来满足自己的需求。
     而贝塞尔曲线,就是其中的一种方式,比如说我们需要画一个弧形的线条或者是图形,这时候就可以使用贝塞尔曲线来进行,最简单的就是画一个圆形。
     下面就是介绍一下贝塞尔曲线:
     贝塞尔曲线,就是通过一个起始点,一个结束点,中间对应不同的数量的条件点组成,每条线的距离可以不同,长度也可以不同,所连接的角度也不同。
     最简单的,就是只有起始点和结束点的,就是一条直线。。。
之后,就是三条线的贝塞尔曲线,根据角度和长度不同可以形成半圆或者是扭曲的圆。
以两条线三点的贝塞尔曲线为例,第一个是起始点,第二个是结束点,中间还有一个条件点,三条线链接形成一个夹角,三个角分为ABC。
然后,我们可以设定为由A到B的时间为0-1,B到C的时间也是0-1,当开始画图的时候,从A到B的点与B到C的点相连成一条线,在这条线上,有个点D以同样的时间进度前进,从A开始,随着进度到C,在这期间所画成的弧线就是贝塞尔曲线。
以图为例,每一条相同颜色 的线,就是连接的线,上面的那一圈黑点就是随着移动在线上的移动位置所经过的位置,当这些点的距离够近并且相连起来的时候,就是一个曲线,每一条的移动距离是不一定的,但是从头到尾的移动时间都是一样的,也就是在0-1之间走完。
比如,那个处于中间位置的蓝色,当A到B的线上走到0.5的位置,那么B到C的距离也一定是0.5,而在那条蓝色的线上的点,处于的位置也一定是0.5,随着增加而增加。
也就是说,所有的点的进度,都是相同的,无论所在的线的长短。
这就是要给二阶的贝塞尔曲线,这样就算是其他更高阶的贝塞尔曲线也是同样的方式处理。
三阶的贝塞尔曲线是三条线,四阶的是四条线:


而运算的方式都是一样的,将两个相近的线相连,三阶的就会成为两条线,在将两条线相连,就会形成第三条线,而第三条线的移动和上面的点经过的位置就是曲线的移动路线,四阶的一样。

这是贝塞尔曲线的运算公式,每次随着增加,运算的平方都会跟着增加,理论上可以无上限,但是我们平时并不会用到那么多就是了,基本上三阶的就够了。



下面就是通过自定义View来实现1~3阶的贝塞尔曲线,因为在系统里直接就有1~3阶的计算公式,我们只要直接调用本身的就可以了。
public class Text_3_2_1 extends View {
public Text_3_2_1(Context context) {
super(context);
init();
}

public Text_3_2_1(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}

public Text_3_2_1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
//画笔
private final Paint mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path mPath=new Path();

//初始化方法
private void init(){



Paint paint=mPaint;


//画笔设置
//抗锯齿
paint.setAntiAlias(true);
//抗抖动
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);


//一阶的贝塞尔曲线只有两个点
//起始点
Path path=mPath;
path.moveTo(50,50);
//结束点
path.lineTo(50,800);


/**
* 二阶贝塞尔曲线,是从上一个的结束,400开始的
* 后面的数是结束点,前面的xy是中轴线,是上一个的结尾和后面的中间位置
* 这种方式适合自己给定固定好的点
*/
// path.quadTo(300,100,400,200);

/**
* 相对的实现二阶贝塞尔曲线,相对于上一次结束的点
* 相对于上一个结束点,先是控制点,对应着结束自己增加与减少,第二个结束点也一样。
* 也就是,用结束点加上数字得到控制点的数字300+100.。。
* 这个不用考虑固定的坐标,只要考虑之后的增减就可以,会随着上一个的移动而自己调节
* 适合不固定的点
*/
// path.rQuadTo(100,-100,200,0);
// path.moveTo(200,400);

/**
* 三阶贝塞尔曲线,有两个控制点,这个是给出固定坐标点的
*第一对是第一个控制点的xy
* 第二对的控制点的XY
* 第三个则是最后的结束点
* 画出一个曲线
*/
// path.cubicTo(300,100,400,400,500,200);
/**
* 相对的实现三阶贝塞尔曲线
* 与二阶的样,进行运算加减
* 50,800)
*/
path.rCubicTo(100,-400,300,-200,500,-400);

}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘画
canvas.drawPath(mPath, mPaint);
//打印控制点
canvas.drawPoint(300,100,mPaint);
canvas.drawPoint(400,400,mPaint);

}
}


而从四阶以上,就需要我们自己进行计算,因为里面就已经没有公式了,而且因为需要循环计算从0-1之间的移动,消耗的资源也是很多的,下面就是代码。
public class BezierView extends View{
public BezierView(Context context) {
super(context);
init();
}

public BezierView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}

public BezierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}


private Path mBezier=new Path();
private Paint mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
public void init(){
Paint paint=mPaint;
//画笔设置
//抗锯齿
paint.setAntiAlias(true);
//抗抖动
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);

//初始化贝塞尔曲线4~~往上
initBezier();
}
private void initBezier(){
//(0,0),(300,300),(200,700),(500,500),(700,1200)
float[] xPoints=new float[]{0,100,200,300,400};
float[] yPoints=new float[]{300,600,100,500,300};

// float pregress=0.2f;//...

Path path=mBezier;

int fps=100;
for (int i=0;i<=100;i++){
//进度
float pregress=i/(float)fps;
float x = calculateBezier(pregress, xPoints);
float y = calculateBezier(pregress, yPoints);
//使用链接的方式,当xy变动足够小的情况下,就是平滑曲线了
Log.e("x...",".."+x);
Log.e("y...",".."+y);
path.lineTo(x,y);
}
}

/**
*
* 计算某时刻的贝塞尔所处的值(xY
* @param t 时间(0~1
* @param values 贝塞尔点集合(xy
* @return 当前t时刻的贝塞尔所处点
*/
private float calculateBezier(float t,float... values){
//才用双层循环
//首先得到当前的长度
final int len=values.length;//初始=4
//外层就是当前长度-1的循环
//i初始=3,核心部分
for (int i=len-1;i>0;i--){
//外层
for (int j=0;j<i;j++){
//内层,进行计算
/**
* 两点之间进行计算的公式
* 第一个点+后一个点减去第一个点的差值乘以时间t
* 算出来的再加上第一个点的就是第一个点到第二个点的距离
*/
values[j]=values[j]+(values[j+1]-values[j])*t;

}
}
//运算时结构保存在第一位
//所以,我们返回第一位
return values[0];
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mBezier,mPaint);
}
}




















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值