一、初始化画笔和设置数据
1、首先初始化一下画笔,然后开启抗锯齿模式并设置画笔类型为描边加填充
paint=new Paint();
//开启抗锯齿
paint.setAntiAlias(true);
//画笔为填充加描边
paint.setStyle(Paint.Style.FILL_AND_STROKE);
2、设置数据
public void setDate(List<ChartBean> list,List<ChartBean> listX){
this.list=list;
this.listX=listX;
if (list.size()==0){
return;
}
if (list.size()==1){
numberY=list.get(0).getValue();
}else {
for (int i = 0; i < list.size(); i++) {
for (int j = 1; j < list.size(); j++) {
if (list.get(i).getValue() > list.get(j).getValue()) {
numberY = list.get(i).getValue();
}
}
}
}
invalidate();//刷新视图
}
list是纵坐标的数据,listX是横坐标的数据,numberY是为了拿到最大值,当时是为了绘画双柱状图或者三柱状图的时候拿到最大值来设置纵坐标的,但是感觉有点瑕疵,因为list和listX都传数据,好像到时候绘画多柱状图的时候在传数据那块步骤会比较多,给你们看看我传的数据:
barChartView=findViewById(R.id.bar);
list.add(new ChartBean("进来",60));
listX.add(new ChartBean("03月10号",60));
listX.add(new ChartBean("03月11号",50));
listX.add(new ChartBean("03月12号",30));
listX.add(new ChartBean("03月13号",10));
listX.add(new ChartBean("03月14号",15));
listX.add(new ChartBean("03月15号",6));
listX.add(new ChartBean("03月16号",18));
barChartView.setDate(list,listX);
后续再对这块进行修改,现在暂时先这样用,传数据进来后不要忘了刷新视图哟,不然会不显示效果哟。
二、绘制纵坐标
1、绘制纵坐标的坐标轴,因为纵坐标中我们的x的起止坐标是不变的,纵坐标是条直线,我们在绘画的时候是要给出起点的坐标(x,y)和终点坐标(x,y),然后根据这两个坐标进行连线。
float startX=60;
float startY=getHeight()-60;
float stopY=30;
paint.setColor(Color.BLACK);
canvas.drawLine(startX,startY,startX,stopY,paint);
先给看效果图,然后再根据效果图解释各个参,1处是整个画布的圆点就是(0,0),2处的画布坐标是(0,getHeight()),getHeight()是你对这个画布设置的高度,4处的画布坐标是(getWidth(),0),getWidth()是你对这个画布设置的宽度,所以3处的画布坐标就是(getWidth(),getHeight()),这就是对画布四个角坐标的解释,接下来看我们绘画纵坐标的起止坐标,就是5,6处,我们结合代码来看,起止坐标的横坐标都是一样的,所以都是startX,我们这里设置的startX等于60,指的是距离左侧的距离是60,你也可以这样理解,1处是圆点(0,0),startX为60,我们可以先找(60,0)这个坐标,我们可以得出是往右平移了60;我们接下来看startY,getHeight()是整个画布的高度,我们假设画布高度是300,减去60,我们就可以得出开始坐标的y轴的值是240,canvas.drawLine(startX,startY,startX,stopY,paint);这几个个参数指的是起止坐标加上画笔,你可以这样看(startX,startY)开始的坐标就是5处,(startX,stopY)结束的坐标就是6处
3、绘画刻度:
我们先取得我们要用来绘制刻度的长度,然后再除以最大值从而获得绘制一个刻度的高度是多少
even=(getHeight()-60-60)/numberY;//一个刻度所占据的高度
然后开始根据一个刻度所占据的高度绘制纵坐标的刻度
//绘画纵坐标的刻度
paint.setTextSize(sizeY);
paint.setTextAlign(Paint.Align.CENTER);
for (int i=1;i<numberY+1;i++){
listY.add(i+"");
}
//canvas.drawText("0",startX-23*3/2,startY,paint);
for (int i=4;i<listY.size();i=i+5){
canvas.drawText(listY.get(i), startX - 23 * 3 / 2, startY - even*(i+1), paint);
}
我这里是根据5的倍数绘画刻度,
canvas.drawText(listY.get(i), startX - 23 * 3 / 2, startY - even*(i+1), paint);第一个参数是刻度值,第二个参数是这个值所在的x轴的坐标,第三个参数是这个值所在的y轴坐标,最后一个参数是画笔。
效果展示:
三、绘制横坐标和柱状图
1、绘制横坐标的坐标轴,跟绘制纵坐标其实是异曲同工之处
float startX=60;
float startY=getHeight()-60;
float stopX=getWidth()-60;
paint.setColor(Color.BLACK);
canvas.drawLine(startX,startY,stopX,startY,paint);
效果展示:
2、绘制横坐标的刻度,其实和纵坐标也是大差不差,但是我这里加了一个东西。原先我的思路是这样的,假设我们取长度100作为横坐标刻度的总长度,然后有五个刻度,100除以5,就得到每个刻度占据20,但是会出现一个情况,当刻度的文字的宽度小于20并且还小于柱状图的宽度,然后我们得到的结果就会是刻度不在柱形的中间是在最左侧,这时候加上宽度的一半然后减去文字本身宽度的一半就可以成功实现在中间,代码如下:
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(15);
for (int i=0;i<listX.size();i++){
float rangeX=(getWidth()-rangeXWidth*2)/listX.size()*i;
float textWidth = paint.measureText(listX.get(i).getName()); // 获取文字的宽度
textList.add(textWidth);
//每个横坐标的宽度
float width=((getWidth()-rangeXWidth*2)/listX.size()*(i+1))-rangeX;
if (width>textWidth && textWidth<barWidth){
rangeX=rangeX+10+barWidth/2-textWidth/2;
}
canvas.drawText(listX.get(i).getName(),startX+rangeX,startY+30,paint);
}
注:barWidth是矩形的宽度
效果展示:
3、绘制矩形
paint.setColor(Color.BLUE);
//绘画柱状图
for (int i=0;i<listX.size();i++){
float left=0,top=0,right=0,bottom=0;
if (textList.get(i)>=30) {//如果横坐标的内容大于柱状图的宽度,就使得柱状图位于横坐标的中心
rangeWidth = (textList.get(i) - 30) / 2;//计算出柱状图的起始位置距离横坐标的起始位置的距离,使得柱状图可以在横坐标的中心位置
}else {
rangeWidth=10;
}
right=(getWidth()-rangeXWidth*2)/listX.size()*i+barWidth+60+rangeWidth;
top= (float) (getHeight()-(60+listX.get(i).getValue()*even));
left=(getWidth()-rangeXWidth*2)/listX.size()*i+ 60+rangeWidth;
bottom=getWidth()-60;
RectF rectf=new RectF(left,top-sizeY/2,right,bottom);
canvas.drawRect(rectf,paint);
}
if (textList.get(i)>=30) {//如果横坐标的内容大于柱状图的宽度,就使得柱状图位于横坐标的中心 rangeWidth = (textList.get(i) - 30) / 2;//计算出柱状图的起始位置距离横坐标的起始位置的距离,使得柱状图可以在横坐标的中心位置 }else { rangeWidth=10; }
这段代码是为了防止第一个矩形的最左侧与纵坐标重叠,并且当刻度的文字宽度大于矩形宽度的时候,矩形的位置在刻度的中间
效果展示:
结语:如果我有任何错误或不准确的地方,请各位大佬批评指正,我将非常感激您的帮助。