喜欢订阅我的公众号--Java2Android技术
Android自定义图表:
效果图:
Demo下载地址:http://download.csdn.net/download/chen364567628/10249231
需求:折线图,曲线图,柱状图,柱线图,让你几分钟搞明白那些框架
思路:柱状图为例
1、自定义ChartView继承View
初始化需要的画笔(Paint)和图(canvas),配置画笔的颜色、大小、指定所需要画的图形。等等
2、测量ChartView高度,并设置边距和内容区域大小
直接调用系统方法即可;
3、根据数据计算每个Chart的间隔
ChartView的高度是x轴文字的高度加上下边距以及y轴的长度。
同理,ChartView的宽度是y轴的文字加上下边距以及x轴的长度。如图所示:
4、直接画出对应的图
调用系统方法
步骤:
1、
public class CombineChart extends View
2、测量view高度,并设置边距和内容区域大小
// 当这个视图的大小改变时,这在布局过程中被调用。 如果
// 你刚刚被添加到视图层次结构,你被称为旧的
// 值为0。
// @param w此视图的当前宽度。
// @参数h此视图的当前高度。
// @param oldw这个视图的旧宽度。
// @param oldh这个视图的旧高度。
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { screenWidth = getMeasuredWidth(); screenHeight = getMeasuredHeight(); //设置矩形的顶部 底部 右边Y轴的3部分每部分的高度 getStatusHeight(); super.onSizeChanged(w, h, oldw, oldh); }
3、初始化画笔
public CombineChart(Context context) {
super(context);
init(context);
}
private void init(Context context) {
// 如果这个视图没有自己做任何绘图,那么设置这个标志
// 允许进一步的优化。 默认情况下,该标志未设置
// 查看,但可以在某些View子类(如ViewGroup)上设置。
//
// 通常,如果您覆盖{@link #onDraw(android.graphics.Canvas)}
// 你应该清除这个标志。
// @param willNotDraw是否这个视图自己绘制
setWillNotDraw(false);//-------------------------------------这个必须有
barPaint = new Paint();
barPaint.setColor(Color.parseColor("#6FC5F4"));//淡蓝色
}
4、测量每一个线的点与点之间的距离,以及矩形的宽度
X轴的长度除以总的item数+空白空间数乘以空白区域宽度(也可以不指定)
5、重写onDraw方法
详情看代码实现
代码:
public class CombineChart extends View {
private int screenWidth, screenHeight;
/**
* item中的最大值
*/
private float maxValueInItems;
/**
* bar的最高值
*/
private float maxHeight;
/**
* 各种画笔 柱形图的 轴的 文本的 线形图的 画点的
*/
private Paint barPaint, textPaint, linePaint, pointPaint;
/**
* 原点的半径
*/
private RectF barRect;
/**
* 左边和上边的边距
*/
private int botMargin, topMargin;
/**
* 每一个bar的宽度
*/
private int barWidth;
/**
* 每个bar之间的距离
*/
private int barSpace;
/**
* x轴 y轴 起始坐标
*/
private float xStartIndex=0;
/**
* 线的路径
*/
Path linePathT;
/**
* 保存bar的左边和右边的x轴坐标点
*/
private List<Integer> leftPoints = new ArrayList<>();
private List<Integer> rightPoints = new ArrayList<>();
//1.数据集合
List<ArrayList<String>> list;
public CombineChart(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CombineChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public CombineChart(Context context) {
super(context);
init(context);
}
private void init(Context context) {
// 如果这个视图没有自己做任何绘图,那么设置这个标志
// 允许进一步的优化。 默认情况下,该标志未设置
// 查看,但可以在某些View子类(如ViewGroup)上设置。
//
// 通常,如果您覆盖{@link #onDraw(android.graphics.Canvas)}
// 你应该清除这个标志。
// @param willNotDraw是否这个视图自己绘制
setWillNotDraw(false);//-------------------------------------这个必须有
barPaint = new Paint();
barPaint.setColor(Color.parseColor("#6FC5F4"));//淡蓝色
textPaint = new Paint();
textPaint.setAntiAlias(true);
linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setStrokeWidth(1);
linePaint.setColor(Color.parseColor("#6FC5F4"));
linePaint.setStyle(Paint.Style.STROKE);
pointPaint = new Paint();
pointPaint.setAntiAlias(true);
pointPaint.setColor(Color.parseColor("#6FC5F4"));
pointPaint.setStyle(Paint.Style.FILL);
barRect = new RectF();//这是柱子
linePathT = new Path();//这是线的路径
botMargin=ScreenUtils.dp2px(context, 20);
topMargin=ScreenUtils.dp2px(context, 30);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
screenWidth = getMeasuredWidth();
screenHeight = getMeasuredHeight();
//设置矩形的顶部 底部 右边Y轴的3部分每部分的高度
getStatusHeight();
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//得到每个bar的宽度
if (list != null) {
getItemsWidth(screenWidth, list.size());
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
leftPoints.clear();
rightPoints.clear();
canvas.drawColor(Color.parseColor("#FFFFFF"));
if (list == null||list.size()==0) return;
//重置路径
linePathT.reset();
linePathT.incReserve(list.size());
//画矩形,划线,花点
drawBars(canvas);
canvas.save();
//画线型图
canvas.drawPath(linePathT, linePaint);
}
private void drawBars(Canvas canvas) {
for (int i = 0; i < list.size(); i++) {
barRect.left = (int) (xStartIndex + barWidth * i + barSpace * (i + 1));
barRect.top = (int) maxHeight - (int) (maxHeight * (Float.parseFloat(list.get(i).get(0)) / maxValueInItems))+topMargin;
barRect.right = barRect.left + barWidth;
leftPoints.add((int) barRect.left);
rightPoints.add((int) barRect.right);
barPaint.setColor(Color.parseColor("#6FC5F4"));
canvas.drawRoundRect(barRect, 30,30,barPaint);
//确定线形图的路径 和 画圆点
canvas.drawCircle(leftPoints.get(i) + barWidth / 2, barRect.top, 5, pointPaint);
//划线,由两个点
if (i == 0) {
linePathT.moveTo(barRect.left + barWidth / 2, barRect.top);
}else {
linePathT.lineTo(barRect.left + barWidth / 2, barRect.top);
}
//计算文本宽度
float tsize = textPaint.measureText(list.get(i).get(1));
textPaint.setTextSize(ScreenUtils.dp2px(getContext(), 14));
Float tx = barRect.left+barWidth / 2-tsize/2;
//点上方
canvas.drawText(list.get(i).get(0),tx, barRect.top-topMargin/3,textPaint);
//X轴
canvas.drawText(list.get(i).get(1),tx,barRect.bottom+topMargin/2,textPaint);
}
}
/**
* 设置矩形的顶部 底部 右边Y轴的3部分每部分的高度
*/
private void getStatusHeight() {
barRect.top =topMargin;
barRect.bottom = screenHeight-botMargin;
maxHeight = barRect.bottom - barRect.top;
}
public void setChartData(List<ArrayList<String>> list) {
if (list==null||list.size()==0){
throw new RuntimeException("数据不能为null");
}
this.list = list;
for (ArrayList<String> item :list){
float val = Float.parseFloat(item.get(0));
if (val>maxValueInItems){
maxValueInItems=val;
}
}
//得到每个bar的宽度
getItemsWidth(screenWidth, list.size());
invalidate();
}
/**
* 设定每个bar的宽度
*
* @param screenWidth
* @param size
*/
private void getItemsWidth(int screenWidth, int size) {
barWidth = screenWidth / (size + 8);//每个柱子的宽度
barSpace = (screenWidth - barWidth * size) / (size + 1);//空白区域的宽度
}
}
有些手机测量每个数据之间的距离需要在
onSizeChanged这个方法中再调用一次,比如华为手机,不然测不到View的高度,所以绘制不出来。如有兼容问题, 有必要下面这个方法需要多处调用。
getItemsWidth(screenWidth, list.size());