分析:使用的是三阶贝赛尔曲线, mCurvePath.cubicTo(mControlDots[0][0], mControlDots[0][1], mControlDots[1][0], mControlDots[1][1] , mDots.get(i + 1).x, mDots.get(i + 1).y),这个API来绘制连线,具体贝塞尔曲线可以参考百度:
public class CurveView extends View { private Paint mAxisPaint; private int mGap; private int mRadius; private Path mCurvePath; private Paint mPaint; private int[] mDataList; private int mMax; private String[] mHorizontalAxis; private int mNormalDotColor = Color.BLACK; private List<Dot> mDots = new ArrayList<>(); private Rect mTextRect; private int mStep; private Paint mDotPaint; public static final float SMOOTH_RATIO = 0.16f; private float[][] mControlDots = new float[2][2]; public CurveView(Context context) { this(context, null); } public CurveView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CurveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(3); mPaint.setColor(Color.BLUE); mAxisPaint = new Paint(); mAxisPaint.setAntiAlias(true); mAxisPaint.setTextSize(20); mAxisPaint.setTextAlign(Paint.Align.CENTER); mDotPaint = new Paint(); mDotPaint.setAntiAlias(true); mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()); mGap = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics()); mTextRect = new Rect(); mCurvePath = new Path(); initView(context); } private void initView(Context context) { } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mDots.clear(); int width = w - getPaddingLeft() - getPaddingRight(); int height = h - getPaddingBottom() - getPaddingTop(); mStep = width / (mDataList.length - 1); mAxisPaint.getTextBounds(mHorizontalAxis[0], 0, mHorizontalAxis[0].length(), mTextRect); int maxBarHeight = height - mTextRect.height() - mGap; float heightRatio = maxBarHeight / mMax; //遍历数据集合 for (int i = 0; i < mDataList.length; i++) { Dot dot = new Dot(); dot.value = mDataList[i]; dot.transformedValue = dot.value * heightRatio; dot.x = mStep * i + getPaddingLeft(); dot.y = getPaddingTop() + maxBarHeight - dot.transformedValue; mDots.add(dot); } //规划曲线路径 for (int i = 0; i < mDataList.length - 1; i++) { //如果是第一个点,就移动到该点 if (i == 0) { mCurvePath.moveTo(mDots.get(0).x, mDots.get(0).y); } //计算贝赛尔曲线的控制点 calculateControlPoint(i); //使用beisaier连接下一个点 mCurvePath.cubicTo(mControlDots[0][0], mControlDots[0][1], mControlDots[1][0], mControlDots[1][1] , mDots.get(i + 1).x, mDots.get(i + 1).y); } } /** * 计算贝塞尔的控制点 * * @param i */ private void calculateControlPoint(int i) { float x1, y1; float x2, y2; //当前一个点 Dot currentDot = mDots.get(i); //下一个点 Dot nextDot; //上一个点 Dot previousDot; //下下一个点 Dot nextNextDot; if (i > 0) { //当i>0,不是第一个点时,获取上一个点 previousDot = mDots.get(i - 1); } else { //当i=0,即为第一个点时,没有上一个点 previousDot = currentDot; } if (i < mDots.size() - 1) { //当i没有遍历到最后一个点时,获取下一个点 nextDot = mDots.get(i + 1); } else { //最后一个点,没有下一个点,就用最后一个点代替 nextDot = currentDot; } if (i < mDots.size() - 2) { //当i没有遍历到倒数第二个点时,获取下下个点 nextNextDot = mDots.get(i + 2); } else { //当遍历到 倒数第二个点时,下下个点不存在,就用下个点代替 nextNextDot = nextDot; } x1 = currentDot.x + SMOOTH_RATIO * (nextDot.x - previousDot.x); y1 = currentDot.y + SMOOTH_RATIO * (nextDot.y - previousDot.y); x2 = nextDot.x - SMOOTH_RATIO * (nextNextDot.x - currentDot.x); y2 = nextDot.y - SMOOTH_RATIO * (nextNextDot.y - currentDot.y); mControlDots[0][0] = x1; mControlDots[0][1] = y1; mControlDots[1][0] = x2; mControlDots[1][1] = y2; } @Override protected void onDraw(Canvas canvas) { canvas.drawPath(mCurvePath, mPaint); //绘制底部坐标文本和点 for (int i = 0; i < mDots.size(); i++) { String axis = mHorizontalAxis[i]; int x = getPaddingLeft() + i * mStep; int y = getHeight() - getPaddingBottom(); canvas.drawText(axis, x, y, mAxisPaint); Dot dot = mDots.get(i); mDotPaint.setColor(mNormalDotColor); //画点 canvas.drawCircle(dot.x, dot.y, mRadius, mDotPaint); } } private class Dot { float x; float y; int value; float transformedValue; } /** * 设置X轴坐标值 * * @param horizontalAxis */ public void setHorizontalAxis(String[] horizontalAxis) { mHorizontalAxis = horizontalAxis; } /** * 设置柱状图数据 * * @param dataList * @param max */ public void setDataList(int[] dataList, int max) { mDataList = dataList; mMax = max; } }