一、基本认识
贝塞尔曲线(Bezier curve)是应用于二维图形程序的数学曲线。
一阶贝塞尔曲线:
是一条直线,只有起点和终点,实现方法:
canvas.drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint) ;
二阶贝塞尔曲线:
有起点和终点、一个控制点的曲线,实现方法:
path.moveTo(startX, startY); //移至起点
path.quadTo(controlX, controlY, endX, endY); //二阶曲线,参数是控制点和终点坐标
三阶贝塞尔曲线:
有起点和终点、两个控制点的曲线,实现方法:
path.moveTo(startX, startY);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, endX, endY); //三阶曲线,参数是控制点1、控制点2和终点坐标
展示一张效果图:
以下我将会实现图中的效果。
二、二阶贝塞尔曲线View
自定义一个SecondBezierView继承自View。
首先,在构造方法中,初始化我们的画笔和其他变量:
public SecondBezierView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setDither(true);
mLinePaint.setColor(Color.parseColor("#454545"));
mBezierPaint = new Paint();
mBezierPaint.setAntiAlias(true);
mBezierPaint.setDither(true);
mBezierPaint.setColor(Color.RED);
mBezierPaint.setStyle(Paint.Style.STROKE);
mBezierPaint.setStrokeWidth(3);
mBezierPath = new Path();
}
然后,在onMeasure方法中明确我们贝塞尔曲线的起始点坐标、终点坐标和控制点坐标,这里让它处在居中的位置:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mStartX = 50;
mStartY = getMeasuredHeight() / 2;
mEndX = getMeasuredWidth() - 50;
mEndY = getMeasuredHeight() / 2;
mControlX = (mStartX + mEndX) / 2;
mControlY = (mStartY + mEndY) / 2;
}
接下来,就该在onDraw方法中绘制我们的曲线了:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mStartX, mStartY, 5, mLinePaint);
canvas.drawText("起点", mStartX - 10, mStartY - 10, mLinePaint);
canvas.drawCircle(mEndX, mEndY, 5, mLinePaint);
canvas.drawText("终点", mEndX - 10, mEndY - 10, mLinePaint);
canvas.drawCircle(mControlX, mControlY, 5, mLinePaint);
canvas.drawText("控制点", mControlX - 10, mControlY -10 , mLinePaint);
canvas.drawLine(mStartX, mStartY, mControlX, mControlY, mLinePaint);
canvas.drawLine(mEndX, mEndY, mControlX, mControlY, mLinePaint);
mBezierPath.reset(); //path重置,去除掉重复绘制时残留下的线条
mBezierPath.moveTo(mStartX, mStartY); //移至起点
mBezierPath.quadTo(mControlX, mControlY, mEndX, mEndY); //二阶贝塞尔曲线,参数是控制点和终点坐标
canvas.drawPath(mBezierPath, mBezierPaint);
}
最后,重写onTouchEvent,让控制点随着我们手指的移动而移动,并在松开手指时(ACTION_UP),让曲线以动画的形式重置:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:
mControlX = event.getX();
mControlY = event.getY();
invalidate();
break;
case MotionEvent.ACTION_UP:
ValueAnimator animX = ValueAnimator.ofFloat(mControlX, (mStartX + mEndX) / 2);
animX.setDuration(400);
animX.setInterpolator(new OvershootInterpolator());
animX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mControlX = (float) animation.getAnimatedValue();
invalidate();
}
});
animX.start();
ValueAnimator animY = ValueAnimator.ofFloat(mControlY, (mStartY + mEndY) / 2);
animY.setDuration(400);
animY.setInterpolator(new OvershootInterpolator());
animY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mControlY = (float) animation.getAnimatedValue();
invalidate();
}
});
animY.start();
break;
}
return true;
}
三、三阶贝塞尔曲线View
具体实现和二阶贝塞尔曲线相似,只不过这里有两个控制点。
在绘制的时候,需要调用三阶的方法:
mBezierPath.reset();
mBezierPath.moveTo(mStartX, mStartY);
mBezierPath.cubicTo(mControlX1, mControlY1, mControlX2, mControlY2, mEndX, mEndY); //三阶贝塞尔曲线,参数是控制点1、控制点2和终点坐标
canvas.drawPath(mBezierPath, mBezierPaint);
而对于控制点的操纵,这里用到了多点触控:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) { //多点触控
case MotionEvent.ACTION_POINTER_DOWN:
mIsSecondPoint = true;
break;
case MotionEvent.ACTION_POINTER_UP:
mIsSecondPoint = false;
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_DOWN:
mControlX1 = event.getX(0);
mControlY1 = event.getY(0);
if (mIsSecondPoint) {
mControlX2 = event.getX(1);
mControlY2 = event.getY(1);
}
invalidate();
break;
......
}
具体请参见我的代码:https://github.com/Yiiip/Bezier
在实际的运用中,贝塞尔曲线可以让你的应用或者控件拥有非常漂亮的特效,和动画结合效果会非常理想。