Android自定义View——扇形统计图的实现代码
发布时间 - 2026-01-10 23:11:10 点击率:次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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# android
# 扇形统计图
# 自定义圆view
# android自定义环形统计图动画
# Android自定义条形对比统计图
# Android自定义View实现多边形统计图示例代码
# Android编程实现canvas绘制饼状统计图功能示例【自动适应条目数量与大小】
# Android编程实现canvas绘制柱状统计图功能【自动计算宽高及分度值、可左右滑动】
# Android 实现会旋转的饼状统计图实例代码
# Android自定义控件横向柱状统计图
# 绑定
# 画线
# 图中
# 的是
# 并到
# 统计图
# 太多
# 下载地址
# 是由
# 是从
# 要把
# 自定义
# 过大
# 往下
# 来实现
# 看吧
# 还行
# 画出
# 变大
# 大家多多
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Java Adapter 适配器模式(类适配器,对象适配器)优缺点对比
如何彻底卸载建站之星软件?
谷歌浏览器下载文件时中断怎么办 Google Chrome下载管理修复
无锡营销型网站制作公司,无锡网选车牌流程?
浅谈javascript alert和confirm的美化
如何快速搭建自助建站会员专属系统?
北京网站制作的公司有哪些,北京白云观官方网站?
Android自定义listview布局实现上拉加载下拉刷新功能
详解阿里云nginx服务器多站点的配置
怎么用AI帮你为初创公司进行市场定位分析?
Laravel怎么使用Intervention Image库处理图片上传和缩放
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭
Laravel怎么清理缓存_Laravel optimize clear命令详解
Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】
Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全
,在苏州找工作,上哪个网站比较好?
Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】
如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?
JavaScript Ajax实现异步通信
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
jQuery 常见小例汇总
如何在阿里云服务器自主搭建网站?
如何在云主机上快速搭建网站?
如何在局域网内绑定自建网站域名?
Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明
Python函数文档自动校验_规范解析【教程】
香港服务器租用费用高吗?如何避免常见误区?
网站建设保证美观性,需要考虑的几点问题!
微信小程序 五星评分(包括半颗星评分)实例代码
如何快速搭建安全的FTP站点?
如何在云指建站中生成FTP站点?
如何为不同团队 ID 动态生成多个“认领值班”按钮
如何在Windows 2008云服务器安全搭建网站?
Linux系统命令中screen命令详解
python中快速进行多个字符替换的方法小结
Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧
如何在景安服务器上快速搭建个人网站?
网站制作大概要多少钱一个,做一个平台网站大概多少钱?
Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
移动端手机网站制作软件,掌上时代,移动端网站的谷歌SEO该如何做?
大学网站设计制作软件有哪些,如何将网站制作成自己app?
如何快速搭建高效简练网站?
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
网站制作企业,网站的banner和导航栏是指什么?
如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】
如何登录建站主机?访问步骤全解析
Laravel怎么实现验证码(Captcha)功能
Laravel如何使用Livewire构建动态组件?(入门代码)

