Android自定义View——扇形统计图的实现代码
程序员文章站
2023-12-16 23:40:28
android 扇形统计图
先看看效果:
看上去如果觉得还行就继续往下看吧!
自定义view
定义成员变量
private int mheig...
android 扇形统计图
先看看效果:
看上去如果觉得还行就继续往下看吧!
自定义view
定义成员变量
private int mheight, mwidth;//宽高 private paint mpaint;//扇形的画笔 private paint mtextpaint;//画文字的画笔 private int centerx, centery;//中心坐标 //"其他"的value //扇形图分成太多快 所以要合并一部分为其他 即图中灰色部分 private double rest; private int maxnum = 5;//扇形图的最大块数 超过的item就合并到其他 string others = "其他";//“其他”块要显示的文字 double total;//数据的总和 double[] datas;//数据集 string[] texts;//每个数据对应的文字集 //颜色 默认的颜色 private int[] mcolors = { color.parsecolor("#ff4081"), color.parsecolor("#ffc0cb"), color.parsecolor("#00ff00"), color.parsecolor("#0066ff"), color.parsecolor("#ffee00") }; private int mtextsize;//文字大小 单位:像素 private int radius = 1000;//半径 在画图时初始化
测量宽高
@override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); //获取宽高 不要设置wrap_content mheight = measurespec.getsize(heightmeasurespec); mwidth = measurespec.getsize(widthmeasurespec); }
画图
@override protected void ondraw(canvas canvas) { super.ondraw(canvas); //无数据 直接返回 if (datas == null || datas.length == 0) return; centerx = (getright() - getleft()) / 2; centery = (getbottom() - gettop()) / 2; int min = mheight > mwidth ? mwidth : mheight; if (radius > min / 2) { radius = (int) ((min - getpaddingtop() - getpaddingbottom()) / 3.5); } //画各个扇形 drawcircle(canvas); //画线与文字 drawlineandtext(canvas); }
画扇形
一个圆形统计图是由许多个扇形组成的,我们根据数据计算出每个扇形的角度即可。注意,画弧度的时候,角度是顺时针变大的!即与我们平时的坐标是反过来的
//画扇形 private void drawcircle(canvas canvas) { int centerx =( getright() - getleft() )/2;//中点 int centery = ( getbottom() - gettop()) /2; rectf rect = new rectf((float) (centerx - radius), centery-radius, centerx+radius,centery+radius);//圆形区域 int start = 0;//扇形开始的角度 for (int i = 0; i < (maxnum<datas.length?maxnum:datas.length); i++) { float angles = (float) ((datas[i] * 1.0f /total) * 360);//计算扇形的角度 mpaint.setcolor(mcolors[i%mcolors.length]);//颜色 canvas.drawarc(rect,start,angles,true,mpaint);//画扇形 start += angles;//下一个扇形开始的角度 } //画"其他"部分 即图中灰色的部分 rest =0;//保存其他部分的value for(int i=maxnum;i<datas.length;i++){ rest+=datas[i]; } float angles = (float) 360 - start;//角度 mpaint.setcolor(color.gray); canvas.drawarc(rect,start,angles,true,mpaint); }
画线条和文字
主要是计算各个点的坐标很烦
这里我画出一个图 大家把代码和图对照理解一下
//画线与文字 private void drawlineandtext(canvas canvas) { int start = 0; //平移画布到中心 所以下面的坐标是从中点开始算起的 canvas.translate(centerx, centery); mpaint.setstrokewidth(4);//线条宽度 //如果数据集过大 那么要合并到其他 for (int i = 0; i < (maxnum < datas.length ? maxnum : datas.length); i++) { float angles = (float) ((datas[i] * 1.0f / total) * 360); //画线条和文字 drawline(canvas, start, angles, texts[i], mcolors[i % mcolors.length]); start += angles; } //画其他部分的线条和文字 if (start < 360)//如果start小于360 说明有其他部分 drawline(canvas, start, 360 - start, others, color.gray); } private void drawline(canvas canvas, int start, float angles, string text, int color) { mpaint.setcolor(color); float stopx, stopy; stopx = (float) ((radius + 40) * math.cos((2 * start + angles) / 2 * math.pi / 180)); stopy = (float) ((radius + 40) * math.sin((2 * start + angles) / 2 * math.pi / 180)); canvas.drawline((float) ((radius - 20) * math.cos((2 * start + angles) / 2 * math.pi / 180)), (float) ((radius - 20) * math.sin((2 * start + angles) / 2 * math.pi / 180)), stopx, stopy, mpaint ); //画横线 int dx;//判断横线是画在左边还是右边 int endx; if (stopx > 0) { endx = (centerx - getpaddingright() - 20); } else { endx = (-centerx + getpaddingleft() + 20); } //画横线 canvas.drawline(stopx, stopy, endx, stopy, mpaint ); dx = (int) (endx - stopx); //测量文字大小 rect rect = new rect(); mtextpaint.gettextbounds(text, 0, text.length(), rect); int w = rect.width(); int h = rect.height(); int offset = 20;//文字在横线的偏移量 //画文字 文字的y坐标值的是文字底部的y坐标 canvas.drawtext(text, 0, text.length(), dx > 0 ? stopx + offset : stopx - w - offset, stopy + h, mtextpaint); //测量百分比大小 string percentage = angles / 3.60 + ""; percentage = percentage.substring(0, percentage.length() > 4 ? 4 : percentage.length()) + "%"; mtextpaint.gettextbounds(percentage, 0, percentage.length(), rect); w = rect.width() - 10; //画百分比 canvas.drawtext(percentage, 0, percentage.length(), dx > 0 ? stopx + offset : stopx - w - offset, stopy - 5, mtextpaint); }
这样我们就已经完成了绘制的工作了,接下来就是绑定数据啦!
大家只要把datas和texts填充好数据就可以啦,最好调用一下invalidate()方法请求重绘(如果已经绘制了)。
我使用了一个内部抽象类来实现数据绑定的:
public abstract class arcviewadapter<t> { public void setdata(list<t> list) { datas = new double[list.size()]; texts = new string[list.size()]; for (int i = 0; i < list.size(); i++) { total += getvalue(list.get(i)); datas[i] = getvalue(list.get(i)); texts[i] = gettext(list.get(i)); } invalidate();//请求重绘 } //通过传来的数据集的某个元素 得到具体的数字 public abstract double getvalue(t t); //通过传来的数据集的某个元素 得到具体的描述 public abstract string gettext(t t); }
在布局文件里面引用这个arcview,然后在mainactivity中绑定数据:
arcview arcview = (arcview) findviewbyid(r.id.arc); list<times> times = new arraylist<>(); for (int i = 6; i > 0; i--) { times t = new times();//这个类就只有两个变量 t.hour = i; t.text = "number"+i; times.add(t); } //初始化适配器 arcview.arcviewadapter myadapter = arcview.new arcviewadapter<times>(){ @override public double getvalue(times times) { return times.hour; } @override public string gettext(times times) { return times.text; } }; myadapter.setdata(times);//绑定数据
大家可以设置各种setter以便在activity中调用,来提高arcview的灵活性,比如设置颜色、半径、最大块数等等。
demo下载地址:sectordiagram_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。