Android自定义View - 贝塞尔曲线简介

本文参考:https://www.jianshu.com/p/0c9b4b681724 https://www.jianshu.com/p/3b2ac05ae3c7

1 定义

贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线。

2 简介
2.1 二阶贝塞尔曲线

二次方贝兹曲线的路径由给定点P0、P1、P2的函数B(t)追踪:
在这里插入图片描述
公式很难理解,但转换成图像就很简单了。
第一步:在平面内找三个不在同一条直线的点,然后依次连接,不闭合。
在这里插入图片描述
第二步:在AB和BC线段上找出点D和点E,使得 AD/AB = BE/BC。
在这里插入图片描述
第三步:连接DE,在DE上寻找点F,F点需要满足:DF/DE = AD/AB = BE/BC。
在这里插入图片描述
第四步:找到所有的F点,将这些点连接起来,结果如下图所示。
在这里插入图片描述

2.2 三阶贝塞尔曲线

P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。公式如下所示:
在这里插入图片描述
转换成图像看一下,其实和二阶贝塞尔曲线大致相同,只是稍微复杂一些。

第一步:平面内选4个不同线的点并且依次用线段连接(也就是三条线)。
在这里插入图片描述
第二步:同二阶贝塞尔曲线一样首先需要在线段上找对应的点(E、F、G),对应的点必须要符合等比的计算规则,计算规则如下:AE/AB = BF/BC = CG/CD;找到对应的点以后接着依次链接EF、FG;接着在EF、FG线段上面继续找点H、I,对应的点依旧要符合等比的计算规则,也就是 EH/EF = FI/FG;最后连接H、I线段,在HI线段上面继续找点J、点J的计算规则需要符合:EH/EF = FI/FG = HJ/HI。

在这里插入图片描述
第三步:将所有的J连接起来即可,最后的曲线效果如下图所示。
在这里插入图片描述

2.3 n阶贝塞尔曲线

n阶贝兹曲线可如下推断。给定点P0、P1、…、Pn,其贝兹曲线即:
在这里插入图片描述
其本质上与二阶,三阶贝塞尔曲线相同,这里再贴一个四阶贝塞尔曲线的图。
在这里插入图片描述

2.4 一阶贝塞尔曲线

给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:
在这里插入图片描述
在这里插入图片描述
你可能会问这一阶贝塞尔曲线有什么用,直线不是想画随便画。其实这还是有用处的,在Android属性动画,系统为我们提供了一个PathInterpolator插值器。这个PathInterpolator里面就有贝塞尔曲线的身影。

3 应用

接下来就是便是再Android系统上的实际应用。

3.1 API
Path方法简介
mPath.moveTo()移动到操作起始点坐标,不会进行绘制,只用于移动移动画笔
mPath.lineTo()从一个点连线到另一个点,用于进行直线绘制
mPath.quadTo(x1, y1, x2, y2)生成二次贝塞尔曲线,(x1,y1) 为控制点,(x2,y2)为结束点
mPath.cubicTo(x1, y1, x2, y2, x3, y3)生成三次贝塞尔曲线, (x1,y1) 为控制点,(x2,y2)为控制点,(x3,y3) 为结束点;即与二阶区别多一个控制点
3.2 二阶贝塞尔曲线
public class TwoBezierView extends View {

    private Context mContext;
    private Paint mPaint;

    private int eventX, eventY;
    private int centerX, centerY;
    private int startX, startY;
    private int endX, endY;

    public TwoBezierView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public TwoBezierView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }


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

    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
        startX = centerX - 250;
        startY = centerY;
        endX = centerX + 250;
        endY = centerY;
        eventX = centerX;
        eventY = centerY - 250;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setColor(Color.GRAY);

        canvas.drawCircle(startX, startY, 8, mPaint);
        canvas.drawCircle(endX, endY, 8, mPaint);
        canvas.drawCircle(eventX, eventY, 8, mPaint);

        mPaint.setStrokeWidth(3);
        canvas.drawLine(startX, startY, eventX, eventY, mPaint);
        canvas.drawLine(eventX, eventY, endX, endY, mPaint);

        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(startX, startY);
        path.quadTo(eventX, eventY, endX, endY);
        canvas.drawPath(path, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:{
                eventX = (int) event.getX();
                eventY = (int) event.getY();
                invalidate();
                break;
            }
        }
        return true;
    }
}

在这里插入图片描述

3.3 三阶贝塞尔曲线
public class ThreeBezierView extends View {

    private Context mContext;

    private Paint mPaint;

    private int leftX, leftY;
    private int rightX, rightY;
    private int startX, startY;
    private int endX, endY;
    private int centerX, centerY;

    private boolean leftOrRight;

    public ThreeBezierView(Context context) {
        super(context);
        init(context);
    }

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

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

    private void init(Context context) {
        mContext = context;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
        startX = centerX - 250;
        startY = centerY;
        leftX = centerX - 250;
        leftY = centerY - 250;
        rightX = centerX + 250;
        rightY = centerY - 250;
        endX = centerX + 250;
        endY = centerY;

        leftOrRight = true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setColor(Color.GRAY);
        canvas.drawCircle(startX, startY, 8, mPaint);
        canvas.drawCircle(leftX, leftY, 8, mPaint);
        canvas.drawCircle(rightX, rightY, 8, mPaint);
        canvas.drawCircle(endX, endY, 8, mPaint);

        mPaint.setStrokeWidth(3);
        canvas.drawLine(startX, startY, leftX, leftY, mPaint);
        canvas.drawLine(leftX, leftY, rightX, rightY, mPaint);
        canvas.drawLine(rightX, rightY, endX, endY, mPaint);

        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(startX, startY);
        path.cubicTo(leftX, leftY, rightX, rightY, endX, endY);
        canvas.drawPath(path, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE: {
                if (leftOrRight) {
                    leftX = (int) event.getX();
                    leftY = (int) event.getY();
                } else {
                    rightX = (int) event.getX();
                    rightY = (int) event.getY();
                }
                invalidate();
                break;
            }
        }
        return true;
    }

    public void setLeft() {
        leftOrRight = true;
    }

    public void setRight() {
        leftOrRight = false;
    }
}

在这里插入图片描述

4总结

贝塞尔曲线本身是比较容易理解的,但是却有广泛的用处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值