自定义View——canvas画曲线图表

最近产品说要在项目中加个图表,虽然Android已经有很多成熟的关于图表的框架,但为了这个项目中一两个图表而多引入一个框架觉得有点浪费,所以就想自己动手写一个。先上图:

这里写图片描述

好在这个图并没有那么复杂,先对这个进行步骤拆分:

  1. 准备工作
  2. 确定动态数据
  3. 确定原点
  4. 绘制X轴
  5. 绘制Y轴
  6. 绘制曲线

1. 准备工作

定义一个类,让它继承自View、定义Paint、初始化…,这部分就不啰嗦了。

2. 确定动态数据

通常这种XY轴的图表只需要给出一组点,按照点的坐标进行绘制即可。这个图也一样需要X轴和Y轴两个坐标,但这个图表里的X轴表示的是日期,每天都绘制一个点,取最近的30个点。因此在传递过来的数据中对于X轴我们要的并非是具体数值,而是开始和结束时间的文字。因此我们定义如下数据bean:

 public static class Data {
        public String dataX;
        public int dataY;
    }

对外提供方法:

 public void setDataSource(List<Data> dataSource) {
        this.dataSource = dataSource;
        invalidate();
    }

为了以后的拓展性,Y轴上的"元"设置为动态添加:

	/**
    * Y坐标的后缀
    * @param suffixY
    */
   public void setSuffixY(String suffixY) {
       this.suffixY = suffixY;
   }

3. 确定原点

由于X轴要右对齐,所以以下图标注的位置作为原点

这里写图片描述

定义两个变量 originX、originY,用来表示原点的XY坐标。下面要对这两个坐标进行计算:
originX=paddingLeft+Y轴text;
originY=View的height-paddingBottom-X轴text;

originY:

  mPaint.getTextBounds(txtLast, 0, txtLast.length(), textRec);
  originY = height - getPaddingBottom() - textRec.height() - 16;

txtLast表示X轴text的文字,末尾的-16是为了让下面的线和文字间有些间距。
originX与originY类似,只是textRec.height()换成了textRec.width():

mPaint.getTextBounds("10元", 0,"10元".length(), textRec);
originX = getPaddingLeft() + textRec.width() + 16;

4. 绘制X轴

在X轴我们只需要绘制X轴上的左右两个坐标(即图中的“6月1日”、“6月30日”),数据已经通过setData传入直接绘制即可:

//绘制X轴坐标(左右各一个)
canvas.drawText(txtFirst, originX, height - getPaddingBottom(), mPaint);
canvas.drawText(txtLast, width - getPaddingRight() - textWidthX,height - getPaddingBottom(), mPaint);

5. 绘制Y轴

图中的Y轴有五个数值,因此我们要在Y轴方向上等分四份,两段线之间的距离我们定义为cellHeight,所以只需要如下绘制即可:

 cellHeight = (originY - getPaddingTop()) / 4;
        for (int i = 0; i < 5; i++) {
            //画横线
            canvas.drawLine(originX, originY - cellHeight * i,
                    width - getPaddingRight(), originY - cellHeight * i, mPaint);
        }

等等,左边还有文字没有绘制,横线是与文字中间对齐的,在for循环中添加如下代码:

mPaint.getTextBounds((10+5* i) + "元", 0, ((10+5* i) + "元").length(), textRec);
canvas.drawText((10+5* i) + "元", originX - textRec.width() - 8, originY - cellHeight * i + textRec.height() / 2, mPaint);

OK,至此Y轴画完,只剩下表格内容。

6. 绘制曲线

由于传入的数据中只包含了Y轴坐标,没有X轴的,但我们知道X轴是连续绘制的,所以我们只需要对X轴进行等分:

 cellWidth = (width - getPaddingRight() - originX) * 1.0f / (dataSource.size() - 1);

*1.0f是为了消除取整时的误差。
这样每个点的XY坐标就都有了,只需要绘制就可以了:

for (int i = 0; i < dataSource.size() - 1; i++) {
           //绘制代码
        }

开始时我使用的是canvas.drawLine,很明显绘制出的是折线图,也想用贝塞尔曲线,但一直不知道公式,因为贝塞尔的参数前面是控制点而不是绘制点。参考了下 https://github.com/mychoices/Jgraph的开源,里面的曲线画法公式这样的:

midX=(startX+endX)/2;
path.cubicTo(midX,startY,midX,endY,endX,endY);

OK,至此曲线图表绘制完成,其他扩展根据自己需要而定。

源码下载:https://github.com/FullCat/Chart

发布了37 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/github_34790294/article/details/53140603