自定义曲线图

最近公司的项目需求要求用曲线图显示数据,开始的时候用的是第三方HelloChart,但是项目部需要动态显示曲线,也就是需要曲线往x轴慢慢的绘制过程,而不是HelloChart直接满x轴显示(我看了下HelloChart的代码,没有发现能满足需求的),所以又花了一个下午的时间自己写出满足需求的曲线图。

思路:

先分析需要哪几个方面:

    1.需要一个背景框架,所以写个ViewFrame类,里面含有画背景框架的属性,例如x,y轴的最大值,每一个坐标 点的的间隔,坐标lable的字体大小,以及颜色等。
    2.一个描述点的类PointValue,x点,y点,考虑是否绘制这个点,设置一个boolean属性。
    3.需要一个类LineData来描述绘制曲线的属性,里面属性包含点的集合,线的宽度,颜色,以及绘制圆点时的颜色半径。
    4.最后就是控件类SmartCurveView,当然这个类持有上述13类的对象,画线的时候根据这些引用对象来绘制。

说说绘制过程:

    @Override
    protected void onDraw(Canvas canvas) {
        if (null != mFrameRect)
            drawViewFrame(canvas);
        if (null != mLineData && null != mDataRect) {
            drawData(canvas);
        }
    }

绘制背景框架部分:

绘制框架时首先先绘制x轴和y轴的线部分,考虑到接下来还需要绘制坐标的lable,所以需要预留一部分空间,因此通过mFrameRect来控制,mDataRect 是绘制曲线的范围。blank 表示预留的宽度,offX和offY是为了画轴时能过相交点的偏移量。

这里写图片描述

       @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mFrameRect = new Rect(blank, 0, w, h - blank);
        mDataRect = new Rect(blank + offX, 0, w, h - blank - offY);
    }

首先是绘制横线和纵线,需要获取x轴和y轴的坐标点,这里定义用ViewFrame中的轴最大值除以坐标间隔,得到轴坐标的点数。

      float[] axisX = new float[(int) (mViewFrame.getAxisXMax() / mViewFrame.getInterXVal()) + 1];
        float[] axisY = new float[(int) (mViewFrame.getAxisYMax() / mViewFrame.getInterYVal()) + 1];

并根据点数求出点在实质在控件中的间隔:

这里写图片描述

  float intevalX = ((float) mFrameRect.width() - offX) / axisX.length; //实质x轴坐标间隔
     float intevalY = ((float) mFrameRect.height() - offY) / axisY.length; //实质y轴坐标间隔

得出坐标的间隔,便可以求出x,y轴的各个点坐标并绘制线。比如 x轴的第n点就是(offX + n * intevalX ,mFrameRect .height()),线段的另一个点(offX + n * intevalX ,0),这样就可以绘制线段。

这里写图片描述

    for (int i = 0; i * intevalX + offX < mFrameRect.width(); i++) {
      axisX[i] = i * intevalX + offX;
      canvas.drawLine(axisX[i] + mFrameRect.left, 0f, axisX[i] + mFrameRect.left
      ,mFrameRect.height(), mFramePaint);
      }

循环中的i * intevalX + offX < mFrameRect.width()条件是为了不绘制最右边的轴线,不然影响美观,(这是我刚开始的时候的逻辑有关,因为我开始认为mViewFrame.getAxisXMax/intervalX的值是需要绘制的线,又因为不想在最右边绘制线所以就加了一个点,算逻辑误区吧),这也是mViewFrame.getInterXVal()) + 1 的原因。

横线同理。
画完基本的线框架,接下来要做的是画出x,y轴各个点的lable。知道x,y轴各个点的坐标可以很快的算出要画的位置:

这里写图片描述

    for (int i = 0; i <= valX; i++) {
     String text = getName(i * mViewFrame.getInterXVal());
     canvas.drawText(text, i * intevalX + offX + blank + text.length() / 2
     ,getHeight() -  blank /2, mTextPaint);
        }

y轴lable同理。

绘制数据点部分:

绘制数据点再把点连成线, 由于要画曲线,采用Path类,并且采用这个类可以在曲线下部分与x轴组成的一个域内绘制成其他颜色。

 private void drawData(Canvas canvas) {
    if (null == mViewFrame)
        return;
    List<PointValue> values = mLineData.getValues();
    drawLine(canvas, values);
    drawPoint(canvas, values);
}

这里写图片描述

绘制数据点时需要做把点的值转化为在控件上的坐标,例如valueX 这个是点的x轴的值, mViewFrame.getDrawAxisXMax()表示我在x轴的画的时候的最大取值值,跟设置的mViewFrame.setAxisXMax()有关(这是我刚开始的时候的逻辑有关,因为我开始认为mViewFrame.getAxisXMax/intervalX的值是需要绘制的线,又因为不想在最右边绘制线所以就加了一个点,算逻辑误区吧)。求出valueX占mViewFrame.getDrawAxisXMax()的比例 。在根据getWidth() - offSetX 控件x轴的宽度。最后通过加上offSetX得出点在x轴的实质位置。

     public float getPointX(float valueX) {
        float offSetX = offX + blank;
        float width = getWidth() - offSetX;//控件绘制点的宽度
        return width * valueX / mViewFrame.getDrawAxisXMax() + offSetX;//根据比例算出点所在的x坐标
    }

y点同理

  public float getPointY(float valueY) {
        float offSetY = offY + blank;
        float height = getHeight() - offSetY;
        return getHeight() - (height * valueY / mViewFrame.getDrawAxisYMax() + offSetY);
    }

完成通过点的值获取坐标就可以通过坐标画线:

 path.cubicTo(fitstX, fitstY, secondX, secondY,currentX, currentY)

这是绘制贝塞尔曲线的方法,首先需要确定3个点才能绘制一条曲线。

这里写图片描述

比如绘制当前点C的曲线时需要得到前前个点A,前个点B,以及下一个点D,求出
控制点first.x = (C.x - A.x) * 0.16f + B.x;first.y = (C.y - A.y) * 0.16f + B.y;
控制点second.x = C.x - (D.x - B.x) * 0.16f; second.y = C.y - (D.y - B.y) * 0.16f;
通过控制点1和控制点2以及当前点就可以画出一条曲线端,其他点同理。需要注意的地方就是前面两个以及最后一个特殊点。比如第二个点为当前点,第一个点位前一个点,那前前一个在数据中没有就取前一个点的值。

这里写图片描述

画完曲线后,path实例已经包含了所画的曲线路径,在通过:

       path.lineTo(right, baseRawValue);
        path.lineTo(left, baseRawValue);
        path.close();

得到一个闭合的路径。这样就可以调用canvas.drawPath方法在区间内画上自己想要的颜色。

画完曲线后就可以画自己想要标志明显的点。PointValue类中有一个boolean的属性,为true时就根据坐标绘制一个特定颜色,特定半径的圆。

      float pointX = getPointX(valueX);
        float pointY = getPointY(valueY);
        canvas.drawCircle(pointX, pointY, mLineData.getCircleR(), mLinePaint);

这样绘制曲线就完成了。现在来看一下效果:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值