您的位置:首页 > 移动开发 > Android开发

Android 自定义饼状图、柱状图、双折线图

2015-12-16 15:59 627 查看

1、概述

关于Android的图表问题,小编一开始是使用第三方的绘图框架Achartengine来绘制,能实现一大部分的图形。可针对公司产品天马行空的想象显然Achartengine已经不能满足现在的项目了。为此小编我只能尝试着自己自定义一些图表类的控件。这里给大家带来可点击的饼状图、可点击以及可滑动的柱状图、双折线图。

2、效果图

废话不多说,先来上效果图。

【1】饼状图



【2】折线图



【3】双折线图



3、完全解析

1、主要是使用canvas画布和各种画笔来实现的。需要注意的就是在各种分辨率手机上的适配效果。这里我们有使用到屏幕密度density这个概念来达到适配的效果。需要在该自定义控件的构造函数里书写如下代码获取屏幕密度。

Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
density = displayMetrics.density;


然后就是初始化各种颜色数据源画笔等,然后再OnDraw方法中进行画线、画矩形、写文字、画圆、画背景等

这里的点击事件就是使用onTouchEvent方法获取点击的X、Y区域从而判断你点击的是哪一部分,再写个点击事件的回调函数即可

2、这里小编看下来扇形图还是有点困难的,所以跟大家解释下扇形图的绘制。

其实这里的扇形图我给它做了个加工让他变成了个环形图。还是画扇形然后再这里贴一个白色的圆,你也可以直接画一个弧度但前者的好处是使得圆形的边缘更加光滑。

float start = 0.0f;
for (int i = 0; i < color.length; i++){
piePaint.setColor(color[i]);//设置颜色
float sweep = (float) (value[i] / totalValue * 360);//计算度数
arraydegrees.add(sweep);
canvas.drawArc(pieOval, start, sweep, true, piePaint);//画扇形
start += sweep;
}
canvas.drawCircle(pieCenterX, pieCenterY, cricle, criclePaint);


第一步其实很简单花几分钟的时间就搞定了 那接下来我们就要画图形旁边的注释了了分为两段线和一段文字。主要是确定文字和线的起始坐标。这里我们讲一下确定长线的起始坐标,剩下的线和文字以长线作为参考坐标即可。那我们怎么来确定长线的其实坐标呢,这里我们要用到三角函数,相信大家都已经忘得差不多了,但对于sin和cos应该不陌生吧。

float lineStartX = pieCenterX + pieRadius * (float) (Math.cos(radians));
float lineStartY = pieCenterY + pieRadius * (float) (Math.sin(radians));


这样获得的坐标点及时该段弧度最外围的中心点。剩下的坐标点就都好确定了。

for (int i = 0; i < value.length; i++){
float sweep = (float) (value[i] / totalValue * 360);//计算度数
float radians = (float) ((start + sweep / 2) / 180 * Math.PI);//扇形中间度数
float lineStartX = pieCenterX + pieRadius * (float) (Math.cos(radians)); float lineStartY = pieCenterY + pieRadius * (float) (Math.sin(radians));
float lineStopX, lineStopY;
lineStopX = pieCenterX + (pieRadius+15*density) * (float) (Math.cos(radians));
lineStopY = pieCenterY + (pieRadius+15*density) * (float) (Math.sin(radians));
if(list_endY.size()>=1&&Math.abs(list_endY.get(i-1)-lineStopY)<textSize){
if(lineStartY >pieCenterY){
lineStopY = list_endY.get(i-1)+textSize;
}else{
lineStopY = list_endY.get(i-1)-textSize;
}
}
canvas.drawLine(lineStartX, lineStartY, lineStopX, lineStopY, linePaint);
if(lineStartX > pieCenterX){
canvas.drawLine(lineStopX, lineStopY, lineStopX+moreLine, lineStopY, linePaint);
lineStopX = lineStopX+moreLine;
}else{
canvas.drawLine(lineStopX, lineStopY, lineStopX-moreLine, lineStopY, linePaint);
lineStopX = lineStopX-moreLine;
}
String itemPercentText = title[i];//数据源百分比
float itemPercentTextLen = textPaint.measureText(itemPercentText);//数据源百分比长度
float percentStartX = lineStopX;
float percentStartY = lineStopY;
if (lineStartX > pieCenterX) {
if(percentStartX+itemPercentTextLen>viewWidth){
percentStartX = viewWidth - itemPercentTextLen;
}
} else {
percentStartX -= itemPercentTextLen;
if(percentStartX<0){
percentStartX = 0;
}
}
if(lineStartY >pieCenterY){
if(percentStartY > viewHeight){
percentStartY = viewHeight;
}
}else{
if(percentStartY-textPaint.getTextSize() <0){
percentStartY = textPaint.getTextSize();
}
}
canvas.drawText(itemPercentText, percentStartX, percentStartY, textPaint);//百分比(文字)
list_startX.add(percentStartX);
list_startY.add(percentStartY-textPaint.getTextSize()-5*density);
list_endX.add(percentStartX+itemPercentTextLen);
list_endY.add(percentStartY+5*density);
start += sweep;
}


到这里我们的注释都已经绘制完毕。接下来大家都应该很关心他们的点击事件。这里的点击事件分为两个部分一个是文字点击另一个是环形图的点击。我们先来说一下比较简单的文字几点,这里小编用了比较土的方法就是在画该文字的时候把它的坐标都保存下来
list_startX.add(percentStartX);

list_startY.add(percentStartY-textPaint.getTextSize()-5*density);

list_endX.add(percentStartX+itemPercentTextLen);

list_endY.add(percentStartY+5*density);
然后再onTouchEvent里判断手指按下的坐标点属于哪个位置。

if(event.getY() >= 0 && event.getY() <= viewHeight && event.getX() >=0 && event.getX() <= viewWidth){
if(list_startX != null && list_startY != null &&list_endX != null && list_endY != null){
for(int i=0;i<list_startX.size();i++){
if(event.getX()>=list_startX.get(i) && event.getX()<=list_endX.get(i) && event.getY()>=list_startY.get(i) && event.getY()<=list_endY.get(i)){
if(listener!=null){
listener.onClick(i);
}
break;
}
}
}
}


接下来的环形区域是比较困难的为此我参考了网上的一些资料。主要的思路是按下时x,y相对于中心点的坐标以及利用三角函数算出按下时的角度这样就知道它属于哪一块。

//按下时候的x,y
downx=event.getX();
downy= event.getY();
//计算按下的x,y与坐标中点的距离
absx= Math.abs(downx-pieCenterX);
absy= Math.abs(downy-pieCenterY);
//按下时x,y相对于中心点的坐标
coordinatex= (downx-pieCenterX);
coordinatey= (pieCenterY-downy);


/**
*
* @param 用于计算正切值的x
* @param 用于计算正切值的y
* @param downx  downy 用于判断按下时候  在第几块
* @return 得到按下时候的角度
*/
protected double getangle(float x,float y,float downx,float downy) {
double angle= 0.0;
if(downx>0&&downy<0){
double c=(double)y/(double)x;
double d=Math.toDegrees(Math.atan(c));
angle=d;
}else if(downx<=0&&downy<=0){
double c=(double)x/(double)y;
double d=Math.toDegrees(Math.atan(c));
angle=d+90;
}else if(downx<0&&downy>=0){
double c=(double)y/(double)x;
double d=Math.toDegrees(Math.atan(c));
angle=180+d;
}else{
double c=(double)x/(double)y;
double d=Math.toDegrees(Math.atan(c));
angle=d+270;
}
return angle;
}


4、总结

鉴于本篇博客是小编的第一篇博客有错误的地方请多多包涵。博客的书写格式也是参考我们的鸿洋大神的,有很多我们值得学习的地方,由于时间比较紧急,很多方面没有顾及到,不懂得同学可以给我留言。谢谢大家。源码稍后奉上。

源码下载

(http://download.csdn.net/detail/wjj930706/9361561)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: