# 自定义View——贝塞尔曲线

## 一、数学中的贝塞尔

### 三阶贝塞尔曲线

P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1，并从P2的方向来到P3。一般不会经过P1或P2；这两个点只是在那里提供方向资讯。P0和P1之间的间距，决定了曲线在转而趋进P2之前，走向P1方向的“长度有多长”。

( PS:以上内容来自 Wike )

## 二、Android中的贝塞尔使用

``@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {     super.onSizeChanged(w, h, oldw, oldh);     mViewWidth = w;     mViewHeight = h;     mWidth = mViewWidth - getPaddingLeft() - getPaddingRight();     mHeight = mViewHeight - getPaddingTop() - getPaddingBottom();     r = Math.min(mWidth,mHeight)*0.4f;     rectF = new RectF(-r,-r,r,r); }   @Override protected void onDraw(Canvas canvas) {     mPaint.setColor(Color.MAGENTA);     mPaint.setStrokeWidth(8);     canvas.translate(mViewWidth/2,mViewHeight/2);     mPath.moveTo(-r/2,0);     mPath.quadTo(0,-r/2,r/2,0);     canvas.drawPath(mPath,mPaint);     mPath.rewind();     mPaint.setColor(Color.GRAY);     mPaint.setStrokeWidth(20);     canvas.drawPoints(new float[]{             start.x,start.y,             end.x,end.y,             control1.x,control1.y     },mPaint); } ``

``@Override protected void onDraw(Canvas canvas) {     mPaint.setColor(Color.MAGENTA);     mPaint.setStrokeWidth(8);     canvas.translate(mViewWidth/2,mViewHeight/2);     mPath.moveTo(-r,0);     mPath.rQuadTo(r/2,-r/8,r,0);     mPath.rQuadTo(r/2,r/8,r,0);     canvas.drawPath(mPath,mPaint);     mPath.rewind(); } ``

• 增加一个圆,以r为半径，(0,0)为圆心
``canvas.drawCircle(0,0,r,mPaint2); ``
• 增加一个连接正弦波两端点的半圆弧
``rectF = new RectF(-r,-r,r,r); mPath.addArc(rectF,0,180); ``

* 更进一步

``/**  * Created by Idtk on 2016/6/19.  * Blog : http://www.idtkm.com  * GitHub : https://github.com/Idtk  * 描述 : 显示百分比注水球  */ public class Bezier extends View {      private Paint mPaint,mPaint2;     private Path mPath = new Path();     protected int mViewWidth,mViewHeight;     protected int mWidth,mHeight;     private float r,rArc,x;     private float percent=0.5f;     private RectF rectF;     private PointF mPointF = new PointF(0,0);      public Bezier2(Context context) {         this(context, null);      }      public Bezier2(Context context, AttributeSet attrs) {         super(context, attrs);          mPaint = new Paint();         mPaint.setColor(Color.BLACK);         mPaint.setStrokeWidth(3);         mPaint.setStyle(Paint.Style.STROKE);         mPaint.setTextSize(100);          mPaint2 = new Paint();         mPaint2.setColor(Color.CYAN);         mPaint2.setStrokeWidth(8);         mPaint2.setStyle(Paint.Style.FILL);      }      @Override     protected void onSizeChanged(int w, int h, int oldw, int oldh) {         super.onSizeChanged(w, h, oldw, oldh);          mViewWidth = w;         mViewHeight = h;          mWidth = mViewWidth - getPaddingLeft() - getPaddingRight();         mHeight = mViewHeight - getPaddingTop() - getPaddingBottom();          r = Math.min(mWidth,mHeight)*0.4f;         rectF = new RectF(-r,-r,r,r);     }      @Override     protected void onDraw(Canvas canvas) { //        super.onDraw(canvas);         canvas.translate(mViewWidth/2,mViewHeight/2);         canvas.drawCircle(0,0,r,mPaint);         rArc = r*(1-2*percent);         double angle= Math.acos((double) rArc/r);         x = r*(float) Math.sin(angle);         mPath.addArc(rectF,90-(float) Math.toDegrees(angle),(float) Math.toDegrees(angle)*2);         mPath.moveTo(-x,rArc);         mPath.rQuadTo(x/2,-r/8,x,0);         mPath.rQuadTo(x/2,r/8,x,0);         canvas.drawPath(mPath,mPaint2);         mPath.rewind();         NumberFormat numberFormat =NumberFormat.getPercentInstance();         numberFormat.setMinimumFractionDigits(1);         textCenter(new String[]{numberFormat.format(percent)},mPaint,canvas,mPointF, Paint.Align.CENTER);     }      /**      * 多行文本居中、居右、居左      * @param strings 文本字符串列表      * @param paint 画笔      * @param canvas 画布      * @param point 点的坐标      * @param align 居中、居右、居左      */     protected void textCenter(String[] strings, Paint paint, Canvas canvas, PointF point, Paint.Align align){         paint.setTextAlign(align);         Paint.FontMetrics fontMetrics= paint.getFontMetrics();         float top = fontMetrics.top;         float bottom = fontMetrics.bottom;         int length = strings.length;         float total = (length-1)*(-top+bottom)+(-fontMetrics.ascent+fontMetrics.descent);         float offset = total/2-bottom;         for (int i = 0; i < length; i++) {             float yAxis = -(length - i - 1) * (-top + bottom) + offset;             canvas.drawText(strings[i], point.x, point.y + yAxis, paint);         }     } } ``

### 2、cubicTo

Path.cubicTo是Android的三次贝塞尔曲线的API，示例如下。

``@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {     super.onSizeChanged(w, h, oldw, oldh);     mViewWidth = w;     mViewHeight = h;     mWidth = mViewWidth - getPaddingLeft() - getPaddingRight();     mHeight = mViewHeight - getPaddingTop() - getPaddingBottom();     r = Math.min(mWidth,mHeight)*0.4f;     start.x = -r;     start.y = 0;     control1.x = -r/2;     control1.y = -r/2;     control2.x = r/2;     control2.y = r/2;     end.x = r;     end.y = 0; } @Override protected void onDraw(Canvas canvas) {     canvas.translate(mViewWidth/2,mViewHeight/2);     mPaint.setColor(Color.MAGENTA);     mPaint.setStrokeWidth(8);     mPath.moveTo(start.x,start.y);     mPath.cubicTo(control1.x,control1.y,control2.x,control2.y,end.x,end.y);     canvas.drawPath(mPath,mPaint);     mPath.rewind();     mPaint.setColor(Color.GRAY);     mPaint.setStrokeWidth(20);     canvas.drawPoints(new float[]{             start.x,start.y,             end.x,end.y,             control1.x,control1.y,             control2.x,control2.y     },mPaint); } ``

``cubicPath.moveTo((cur.x-xAxisData.getMinimum())*xAxisData.getAxisScale(),         -(cur.y-yAxisData.getMinimum())*yAxisData.getAxisScale()*animatedValue);  for (int j=1; j< curveData.getValue().size(); j++){     prevPrev = curveData.getValue().get(j == 1 ? 0 : j - 2);     prev = curveData.getValue().get(j-1);     cur = curveData.getValue().get(j);     next = curveData.getValue().size() > j+1 ? curveData.getValue().get(j+1) : cur;     prevDx = (cur.x-prevPrev.x)*intensity*xAxisData.getAxisScale();     prevDy = (cur.y-prevPrev.y)*intensity*yAxisData.getAxisScale();     curDx = (next.x-prev.x)*intensity*xAxisData.getAxisScale();     curDy = (next.y-prev.y)*intensity*yAxisData.getAxisScale();     cubicPath.cubicTo((prev.x-xAxisData.getMinimum())*xAxisData.getAxisScale()+prevDx,             -(((prev.y-yAxisData.getMinimum())*yAxisData.getAxisScale()+prevDy)*animatedValue),             ((cur.x-xAxisData.getMinimum())*xAxisData.getAxisScale()-curDx),             -(((cur.y-yAxisData.getMinimum())*yAxisData.getAxisScale()-curDy)*animatedValue),             ((cur.x-xAxisData.getMinimum())*xAxisData.getAxisScale()),             -(((cur.y-yAxisData.getMinimum())*yAxisData.getAxisScale())*animatedValue)); } canvas.save(); canvas.translate(offset,0); cubicPaint.setColor(curveData.getColor()); canvas.drawPath(cubicPath,cubicPaint); cubicPath.rewind(); ``

## 三、小结

• QQ的拖拽小红点
• 饿了吗点餐动画
• 水滴效果
• 平滑曲线
• 弹性效果

GitHub: https://github.com/Idtk