继承View基类,画了这样的扇形图

直接来步骤吧

(参考了GcsSloop的教程)

1.分析

自定义View需要认真的分析下,里面还是会用到一些数学知识

  • 首先是扇形该怎么表现

    1. 扇形的外观是个圆弧,而且是围绕一个中心点旋转一定角度构成的

    2. View主要的绘制都是通过画布(canvas)进行,canvas里提供了许多绘制的方法。其中有一个

    drawArc()画圆弧的方法。

      public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint) {
    drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,paint);
    }

这个方法就给画扇形提供了捷径。

  • 发现里面有个RectF参数,隐约有点是矩形的感觉,网查一下:

    这个类包含一个矩形的四个单精度浮点坐标。矩形通过上下左右4个边的坐标来表示一个矩形

参数说明一下:

  • oval :指定圆弧的外轮廓矩形区域
  • startAngle: 圆弧起始角度,单位为度。
  • sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度。
  • useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。
  • paint: 绘制圆弧的画笔属性,如颜色,是否填充等。

1.1 分析需要哪些数据

参照上面的画圆弧函数的参数需求和上面那个结(cu)果(cao)图

要使用canvas画扇形需要这些参数吧

  • 外轮廓矩形
  • 起始角度
  • 扫过的角度
  • 画笔

再看上面那个结(cu)果(cao)图,还需要

  • 颜色
  • 文字

这样就可以定义一个数据结构了

public class ViewData {
public String name; //名字
public int value; //数值 public int color; //颜色
public float percentage; //百分比
public float angle; //角度 public ViewData(int value, String name) {
this.value = value;
this.name = name;
}
}

1.2 角度计算

扇形图应该反应的是里面每块扇形占总数的百分比,在我们的数据结构里应该是通过数值(value)计算出扇形区域占总体的比例和扫过的角度

  • 比例 = 数值 / 各块数值之和
  • 角度 = 比例 x 圆周(360度)

1.3 文字位置确定

文字不像扇形区域可以直接调用画布的圆弧方法绘制,文字使用画布的drawText()方法

public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
......
}

查看源码可知需要传递字符内容、x坐标、y坐标和画笔,字符内容和画笔容易,就是坐标位置我们要好好想想

需要注意这里的文字的位置坐标都是基于canvas为父元素的

回到以前的数学应用题,先摆出我们有哪些已知量

  • 扇形圆的半径
  • 每块扇形的扫过的角度
  • 起始的角度

想想根据这些数据,应该可以求出文字位置(上图黑圆点)的坐标

  • 文字位置角度 = 起始角度 + 扇形块角度/2
  • 文字x坐标 = 半径/2 x cos(文字位置角度)
  • 文字y坐标 = 半径/2 x sin(文字位置角度)

分析差不多了,开始实践了

2 实践

2.1 数据结构

public class ViewData {
public String name; //名字
public int value; //数值 public int color; //颜色
public float percentage; //百分比
public float angle; //角度 public ViewData(int value, String name) {
this.value = value;
this.name = name;
}
}

2.2 View实现

先来一张图镇店

public class MyView extends View {
private int[] mColors = {Color.BLUE, Color.DKGRAY, Color.CYAN, Color.RED, Color.GREEN};
private Paint paint; //画笔
private ArrayList<ViewData> viewDatas; //数据集
private int w; //View宽高
private int h;
private RectF rectF; //矩形 public MyView(Context context) {
super(context);
initPaint(); //设置画笔
} //设置数据
public void setData(ArrayList<ViewData> viewDatas) {
this.viewDatas = viewDatas;
initData(); //设置数据的百分度和角度
invalidate(); //刷新UI
} public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
} public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
} //初始化画笔
private void initPaint() {
paint = new Paint();
//设置画笔默认颜色
paint.setColor(Color.WHITE);
//设置画笔模式:填充
paint.setStyle(Paint.Style.FILL);
//
paint.setTextSize(30);
//初始化区域
rectF = new RectF();
} //确定View大小
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.w = w; //获取宽高
this.h = h;
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(w / 2, h / 2); //将画布坐标原点移到中心位置
float currentStartAngle = 0; //起始角度
float r = (float) (Math.min(w, h) / 2); //饼状图半径(取宽高里最小的值)
rectF.set(-r, -r, r, r); //设置将要用来画扇形的矩形的轮廓
for (int i = 0; i < viewDatas.size(); i++) {
ViewData viewData = viewDatas.get(i);
paint.setColor(viewData.color);
//绘制扇形(通过绘制圆弧)
canvas.drawArc(rectF, currentStartAngle, viewData.angle, true, paint);
//绘制扇形上文字
float textAngle = currentStartAngle + viewData.angle / 2; //计算文字位置角度
paint.setColor(Color.BLACK);
float x = (float) (r / 2 * Math.cos(textAngle * Math.PI / 180)); //计算文字位置坐标
float y = (float) (r / 2 * Math.sin(textAngle * Math.PI / 180));
paint.setColor(Color.YELLOW); //文字颜色
canvas.drawText(viewData.name, x, y, paint); //绘制文字 currentStartAngle += viewData.angle; //改变起始角度
}
} private void initData() {
if (null == viewDatas || viewDatas.size() == 0) {
return;
} float sumValue = 0; //数值和
for (int i = 0; i < viewDatas.size(); i++) {
ViewData viewData = viewDatas.get(i);
sumValue += viewData.value;
int j = i % mColors.length; //设置颜色
viewData.color = mColors[j];
} for (ViewData data : viewDatas) {
float percentage = data.value / sumValue; //计算百分比
float angle = percentage * 360; //对应的角度
data.percentage = percentage;
data.angle = angle;
}
}
}

最终实现效果就是开(cu)头(cao)那张了,自定义View还有很长路要走

完整代码请移步这里

分析实现Android自定义View之扇形图的更多相关文章

  1. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  2. Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程

    转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...

  3. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  4. Android 自定义 View 圆形进度条总结

    Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...

  5. Android自定义View(CustomCalendar-定制日历控件)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/54020386 本文出自:[openXu的博客] 目录: 1分析 2自定义属性 3onMeas ...

  6. Android自定义View(RollWeekView-炫酷的星期日期选择控件)

    转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53420889 本文出自:[openXu的博客] 目录: 1分析 2定义控件布局 3定义Cus ...

  7. 我的Android进阶之旅------>Android自定义View实现带数字的进度条(NumberProgressBar)

    今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢 ...

  8. android自定义view系列:认识activity结构

    标签: android 自定义view activity 开发中虽然我们调用Activity的setContentView(R.layout.activity_main)方法显示View视图,但是vi ...

  9. android 自定义view 前的基础知识

    本篇文章是自己自学自定义view前的准备,具体参考资料来自 Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了 ...

随机推荐

  1. .Net底层剖析目录章节

    [.Net底层剖析]目录章节 1.[深入浅出.Net IL]1.一个For循环引发的IL 2.[.Net底层剖析]2.stfld指令-给对象的字段赋值 3.[.Net底层剖析]3.用IL来理解属性 作 ...

  2. list2json

    list2json方法 System.Web.Script.Serialization.JavaScriptSerializer serial = new System.Web.Script.Seri ...

  3. [IR] Tolerant Retrieval & Spelling Correction & Language Model

    Dictionary不一定是个list,它可以是多种形式. 放弃Hash的原因: 通常,tree是比较适合的结构. From: http://www.cnblogs.com/v-July-v/arch ...

  4. 基于openssl的单向和双向认证

    1.前言 最近工作涉及到https,需要修改nginx的openssl模块,引入keyless方案.关于keyless可以参考CloudFlare的官方博客: https://blog.cloudfl ...

  5. iOS实现图像的反色,怀旧,色彩直方图效果

    反色是与原色叠加可以变为白色的颜色,即用白色(RGB:1.0,1.0,1.0)减去原色的颜色.比如说红色(RGB:1.0,0,0)的反色是青色(0,1.0,1.0).在OPENGL ES中为1. 通过 ...

  6. 高级四则运算器—结对项目总结(193 &105)

    高级四则运算器—结对项目总结 为了将感想与项目经验体会分割一下,特在此新开一篇博文. 界面设计 啥都不说,先上图震慑一下... 上面的三个界面是我们本次结对项目的主界面,恩,我也觉得挺漂亮的!你问我界 ...

  7. JavaScript学习总结 Ajax和Http状态字

    Ajax及其工作原理 AJAX 是一种与服务器交换数据无需刷新网页的技术,最早由Google公司在谷歌地图里使用,并迅速风靡. AJAX是不能跨域的,如需跨域,可以使用document.domain= ...

  8. 【助教】Java获取数据库数据展示

    本文将给出一个最简单的Java查询数据库中一张表的数据并将查询结果展示在页面的例子. 实际上,我们要解决以下两个问题: Java与数据库交互(以JDBC为例) 数据展示在前台页面(以Servlet+J ...

  9. SQL Server视图复习

    视图的好处: 第一点:使用视图,可以定制用户数据,聚焦特定的数据. 第一点:使用视图,可以定制用户数据,聚焦特定的数据. 在实际过程中,公司有不同角色的工作人员,我们以销售公司为例的话,采购人员,可以 ...

  10. .Net配置文件——反射+配置文件存储类型实例

    配置文件+反射确实去除了选择语句的繁琐,带来了优美的赶脚! 首先改进了一下类(接上文): ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...