本文主要讲解怎么用贝塞尔曲线将多点连成一条平滑的曲线,若不了解贝塞尔曲线的同学可以查看这里

先看效果

不带辅助线 带辅助线

确定控制点

使用贝塞尔曲线,我们就要先找出它的控制点,笔者Google后发现了此篇文章,文章中列出了找出控制点的公式 找出控制点

注意:

  1. 公式中的当前点是线段的起点,文中的当前点指的是线段的终点
  2. 公式中的a,b代表曲线的弯曲指数,越大代表曲线越弯,一般设置0.16

从公式中可以发现,确定当前点的控制点,需要用到当前点的前两个点和下一个点四个点,那么当当前点是第一个点,第二个点和最后一个点时,我们就无法获取到全部点,那篇文章中又指出 找出控制点

第二种方法过于复杂,文中使用的是第一中方法,即用当前点的值表示无法获取到的点的值, 下面列出代码:

private void measurePath() {
    //保存曲线路径
    mPath = new Path();
    //保存辅助线路径
    mAssistPath = new Path();
    float prePreviousPointX = Float.NaN;
    float prePreviousPointY = Float.NaN;
    float previousPointX = Float.NaN;
    float previousPointY = Float.NaN;
    float currentPointX = Float.NaN;
    float currentPointY = Float.NaN;
    float nextPointX;
    float nextPointY;

    final int lineSize = mPointList.size();
    for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {
        if (Float.isNaN(currentPointX)) {
            Point point = mPointList.get(valueIndex);
            currentPointX = point.x;
            currentPointY = point.y;
        }
        if (Float.isNaN(previousPointX)) {
            //是否是第一个点
            if (valueIndex > 0) {
                Point point = mPointList.get(valueIndex - 1);
                previousPointX = point.x;
                previousPointY = point.y;
            } else {
                //是的话就用当前点表示上一个点
                previousPointX = currentPointX;
                previousPointY = currentPointY;
            }
        }

        if (Float.isNaN(prePreviousPointX)) {
            //是否是前两个点
            if (valueIndex > 1) {
                Point point = mPointList.get(valueIndex - 2);
                prePreviousPointX = point.x;
                prePreviousPointY = point.y;
            } else {
                //是的话就用当前点表示上上个点
                prePreviousPointX = previousPointX;
                prePreviousPointY = previousPointY;
            }
        }

        // 判断是不是最后一个点了
        if (valueIndex < lineSize - 1) {
            Point point = mPointList.get(valueIndex + 1);
            nextPointX = point.x;
            nextPointY = point.y;
        } else {
            //是的话就用当前点表示下一个点
            nextPointX = currentPointX;
            nextPointY = currentPointY;
        }

        if (valueIndex == 0) {
            // 将Path移动到开始点
            mPath.moveTo(currentPointX, currentPointY);
            mAssistPath.moveTo(currentPointX, currentPointY);
        } else {
            // 求出控制点坐标
            final float firstDiffX = (currentPointX - prePreviousPointX);
            final float firstDiffY = (currentPointY - prePreviousPointY);
            final float secondDiffX = (nextPointX - previousPointX);
            final float secondDiffY = (nextPointY - previousPointY);
            final float firstControlPointX = previousPointX + (lineSmoothness * firstDiffX);
            final float firstControlPointY = previousPointY + (lineSmoothness * firstDiffY);
            final float secondControlPointX = currentPointX - (lineSmoothness * secondDiffX);
            final float secondControlPointY = currentPointY - (lineSmoothness * secondDiffY);
            //画出曲线
            mPath.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,
                    currentPointX, currentPointY);
            //将控制点保存到辅助路径上
            mAssistPath.lineTo(firstControlPointX, firstControlPointY);
            mAssistPath.lineTo(secondControlPointX, secondControlPointY);
            mAssistPath.lineTo(currentPointX, currentPointY);
        }

        // 更新值,
        prePreviousPointX = previousPointX;
        prePreviousPointY = previousPointY;
        previousPointX = currentPointX;
        previousPointY = currentPointY;
        currentPointX = nextPointX;
        currentPointY = nextPointY;
    }
    mPathMeasure = new PathMeasure(mPath, false);
}