Android 贝塞尔曲线菜鸟起步

一、基本认识

贝塞尔曲线(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

在实际的运用中,贝塞尔曲线可以让你的应用或者控件拥有非常漂亮的特效,和动画结合效果会非常理想。

猜你喜欢

转载自blog.csdn.net/skipperkevin/article/details/59565584
今日推荐