自定义控件之曲线图

分析:使用的是三阶贝赛尔曲线, 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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值