美文网首页学习Android技术知识Android知识
Android自定义控件(三 .1)Path贝塞尔曲线

Android自定义控件(三 .1)Path贝塞尔曲线

作者: Jackson杰 | 来源:发表于2017-04-16 23:25 被阅读195次

    前一篇文章讲了Path绘制直线以及各种基本图形,这篇文主要说Path绘制曲线,就是曲线中非常有名的贝赛尔曲线。
    贝赛尔曲线是由法国数学家Pierre Bézier所发明,由此为计算机矢量图形学奠定了基础。它的主要意义在于无论是直线或曲线都能在数学上予以描述。

    Paste_Image.png

    贝塞尔曲线作用十分广泛:

    • QQ小红点拖拽效果
    • 一些炫酷的下拉刷新控件
    • 阅读软件的翻书效果
    • 一些平滑的折线图的制作
    • 很多炫酷的动画效果
    理解贝塞尔曲线

    塞尔曲线由起始点、数据点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。
    数据点:确定曲线的起始和结束位置
    控制点:确定曲线的弯曲程度

    • 一阶曲线原理:
      一阶曲线是没有控制点的,仅有两个数据点(A 和 B),最终效果一个线段。
    image.gif
    • 二阶曲线原理:
      二阶曲线由两个数据点(A 和 C),一个控制点(B)来描述曲线状态,大致如下:
    Paste_Image.png

    上图中红色曲线部分就是传说中的二阶贝塞尔曲线,那么这条红色曲线是如何生成的呢?接下来我们就以其中的一个状态分析一下:

    Paste_Image.png
    连接AB BC,并在AB上取点D,BC上取点E,使其满足条件: Paste_Image.png
    连接DE,取点F,使得:
    2222.gif
    • 三阶曲线原理:
      三阶曲线由两个数据点(A 和 D),两个控制点(B 和 C)来描述曲线状态,如下:
      三阶曲线计算过程与二阶类似,具体可以见下图动态效果:
    333.gif

    贝塞尔曲线常用操作速查表

    贝塞尔曲线 对应的方法 演示动画
    一阶曲线(线性曲线) lineTo
    二阶曲线 quadTo
    三阶曲线 cubicTo
    四阶曲线
    了解贝塞尔曲线相关函数使用方法
    • 一阶曲线:参照上篇文章Path的操作
    • 二阶曲线:二阶曲线是由两个数据点,一个控制点构成,两个数据点是控制贝塞尔曲线开始和结束的位置,比较容易理解,而控制点则是控制贝塞尔的弯曲状态,相对来说比较难以理解,所以本示例重点在于理解贝塞尔曲线弯曲状态与控制点的关系。直接上代码:
    public class BezierTwo extends View {
    
        private Paint mPaint;
        private int centerX, centerY;
        private PointF start, end, control; //起点,结束点,控制点
    
        public BezierTwo(Context context) {
            super(context);
            init();
        }
    
        public BezierTwo(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public BezierTwo(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mPaint = new Paint();
            mPaint.setColor(Color.RED);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(8);
            //初始化起点,结束点,控制点
            start = new PointF(0, 0);
            end = new PointF(0, 0);
            control = new PointF(0, 0);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            centerX = w / 2;
            centerY = h / 2;
            //重新设置起点,结束点和控制点的位置
            start.x = centerX - 200;
            start.y = centerY;
            end.x = centerY + 200;
            end.y = centerY;
            control.x = centerX;
            control.y = centerY - 100;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            //根据触摸点更新控制点,并重绘
            control.x = event.getX();
            control.y = event.getY();
            invalidate();
            return true;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //绘制数据点和控制点
            mPaint.setColor(Color.GRAY);
            mPaint.setStrokeWidth(20);
            canvas.drawPoint(start.x, start.y, mPaint);
            canvas.drawPoint(end.x, end.y, mPaint);
            canvas.drawPoint(control.x, control.y, mPaint);
    
            //绘制辅助线
            mPaint.setStrokeWidth(4);
            canvas.drawLine(start.x,start.y,control.x,control.y,mPaint);
            canvas.drawLine(end.x,end.y,control.x,control.y,mPaint);
    
            //绘制贝赛尔曲线
            mPaint.setStrokeWidth(8);
            mPaint.setColor(Color.RED);
            Path path=new Path();
            path.moveTo(start.x,start.y);
            path.quadTo(control.x,control.y,end.x,end.y);
            canvas.drawPath(path,mPaint);
        }
    }
    

    效果图如下:


    为了更直观,上图还绘制了控制点和辅助线,从效果图可以看出,贝赛尔曲线是有类似于橡皮筋的效果,所以经常用于绘制一些具有弹性的效果中。

    • 三阶曲线:三阶曲线由两个数据点和两个控制点来控制曲线状态。
    public class BezierThree extends View {
    
        private Paint mPaint;
        private int centerX, centerY;
        private PointF start, end, control1, control2;
        private boolean mode = true;
    
        public BezierThree(Context context) {
            super(context);
            init();
        }
    
        public BezierThree(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public BezierThree(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mPaint = new Paint();
            mPaint.setColor(Color.GRAY);
            mPaint.setStrokeWidth(8);
            mPaint.setStyle(Paint.Style.STROKE);
    
            start = new PointF(0, 0);
            end = new PointF(0, 0);
            control1 = new PointF(0, 0);
            control2 = new PointF(0, 0);
        }
    
        public void setMode(boolean mode) {
            this.mode = mode;
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            centerX = w / 2;
            centerY = h / 2;
            //初始化数据点和控制点
            start.x = centerX - 200;
            start.y = centerY;
            end.x = centerX + 200;
            end.y = centerY;
            control1.x = centerX;
            control1.y = centerY - 100;
            control2.x = centerX;
            control2.y = centerY - 100;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            //根据触摸位置更新控制点并重绘
            if (mode) {
                control1.x = event.getX();
                control1.y = event.getY();
            }else {
                control2.x=event.getX();
                control2.y=event.getY();
            }
            invalidate();
            return true;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //绘制数据点和控制点
            mPaint.setColor(Color.GRAY);
            mPaint.setStrokeWidth(20);
            canvas.drawPoint(start.x,start.y,mPaint);
            canvas.drawPoint(end.x,end.y,mPaint);
            canvas.drawPoint(control1.x,control1.y,mPaint);
            canvas.drawPoint(control2.x,control2.y,mPaint);
    
            //绘制辅助线
            mPaint.setStrokeWidth(4);
            canvas.drawLine(start.x,start.y,control1.x,control1.y,mPaint);
            canvas.drawLine(control1.x,control1.y,control2.x,control2.y,mPaint);
            canvas.drawLine(control2.x,control2.y,end.x,end.y,mPaint);
    
            // 绘制贝塞尔曲线
            mPaint.setColor(Color.RED);
            mPaint.setStrokeWidth(8);
    
            Path path=new Path();
            path.moveTo(start.x,start.y);
            path.cubicTo(control1.x,control1.y,control2.x,control2.y,end.x,end.y);
            canvas.drawPath(path,mPaint);
        }
    

    效果如图:


    源码下载:
    https://github.com/baojie0327/ViewAndGroup

    相关文章

      网友评论

        本文标题:Android自定义控件(三 .1)Path贝塞尔曲线

        本文链接:https://www.haomeiwen.com/subject/znfgattx.html