当前位置:   article > 正文

Android自定义View——饼图_安卓用view绘制饼图

安卓用view绘制饼图

前段时间项目里面需要用到统计图来分析数据,里面有一张饼图,博主也是第一次画饼图,一开始想偷点懒在网上找了一些资料,

结果并不是很满意,只好自己根据需求设计一张饼图了。

先看看效果图:


接下来是实现原理,这里代码里面的注释比较详尽了,就不做过多说明了。

  1. public class PieChartView extends View {
  2. private Context ctx;
  3. private DecimalFormat format;
  4. private List<PieChartData> mList;
  5. // 画笔
  6. private Paint arcPaint;
  7. private Paint linePaint;
  8. private Paint textPaint;
  9. // 圆心
  10. private float centerX;
  11. private float centerY;
  12. // 大圆半径
  13. private float radius;
  14. // 小圆半径
  15. private float radius_cover;
  16. private float total;
  17. // 初始角度
  18. private float startAngle;
  19. private float textAngle;
  20. private List<PointF[]> lineList;
  21. private List<PointF> textList;
  22. public PieChartView(Context context) {
  23. this(context, null);
  24. }
  25. public PieChartView(Context context, AttributeSet attrs) {
  26. this(context, attrs, 0);
  27. }
  28. public PieChartView(Context context, AttributeSet attrs, int defStyleAttr) {
  29. super(context, attrs, defStyleAttr);
  30. init(context);
  31. }
  32. private void init(Context context) {
  33. ctx = context;
  34. lineList = new ArrayList<>();
  35. textList = new ArrayList<>();
  36. mList = new ArrayList<>();
  37. // 取整
  38. format = new DecimalFormat("##0");
  39. // 扇形画笔
  40. arcPaint = new Paint();
  41. // 抗锯齿
  42. arcPaint.setAntiAlias(true);
  43. // 防抖动
  44. arcPaint.setDither(true);
  45. // 设置画笔风格,实心
  46. arcPaint.setStyle(Paint.Style.FILL);
  47. // 间隔线画笔
  48. linePaint = new Paint();
  49. linePaint.setAntiAlias(true);
  50. linePaint.setDither(true);
  51. linePaint.setStyle(Paint.Style.STROKE);
  52. linePaint.setStrokeWidth(DisplayUtil.dip2px(ctx, 2));
  53. // 文本画笔
  54. textPaint = new Paint();
  55. textPaint.setAntiAlias(true);
  56. textPaint.setDither(true);
  57. textPaint.setStyle(Paint.Style.FILL);
  58. }
  59. @Override
  60. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  61. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  62. int width;
  63. int height;
  64. int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
  65. int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
  66. int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
  67. int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
  68. // 取屏幕宽度与屏幕高度的较小值
  69. if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.EXACTLY) {
  70. height = heightSpecSize;
  71. width = Math.min(heightSpecSize, Math.min(DisplayUtil.getScreenSize(ctx)[0],
  72. DisplayUtil.getScreenSize(ctx)[1]));
  73. } else if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.AT_MOST) {
  74. width = widthSpecSize;
  75. height = Math.min(widthSpecSize, Math.min(DisplayUtil.getScreenSize(ctx)[0],
  76. DisplayUtil.getScreenSize(ctx)[1]));
  77. } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
  78. width = height = Math.min(DisplayUtil.getScreenSize(ctx)[0],
  79. DisplayUtil.getScreenSize(ctx)[1]);
  80. } else {
  81. width = widthSpecSize;
  82. height = heightSpecSize;
  83. }
  84. setMeasuredDimension(width, height);
  85. }
  86. @Override
  87. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  88. super.onSizeChanged(w, h, oldw, oldh);
  89. centerX = w / 2;
  90. centerY = h / 2;
  91. }
  92. @SuppressLint("DrawAllocation")
  93. @Override
  94. protected void onDraw(Canvas canvas) {
  95. super.onDraw(canvas);
  96. textList.clear();
  97. lineList.clear();
  98. lineList = new ArrayList<>();
  99. textList = new ArrayList<>();
  100. if (mList != null) {
  101. // 初始化缩放系数
  102. float k = 1.0f;
  103. // 开始画各个扇形
  104. for (int i = 0; i < mList.size(); i++) {
  105. // 限定饼图所在矩形大小
  106. float left = centerX - radius * k; // 矩形左边的X坐标
  107. float top = centerY - radius * k; // 矩形顶部的Y坐标
  108. float right = centerX + radius * k; // 矩形右边的X坐标
  109. float bottom = centerY + radius * k; // 矩形底部的Y坐标
  110. RectF mRectF = new RectF(left, top, right, bottom);
  111. // 设置扇形颜色
  112. arcPaint.setColor(mList.get(i).color);
  113. // 绘制扇形,参数:画布大小,起始角度,扇形角度,是否描边,画笔属性
  114. canvas.drawArc(mRectF, startAngle, mList.get(i).percent / total * 360f, true, arcPaint);
  115. // 记录间隔线位置
  116. lineList.add(getLinePointF());
  117. // 记录文本位置
  118. textAngle = startAngle + mList.get(i).percent / total * 360f / 2;
  119. textList.add(getTextPointF(k));
  120. //重新计算起始角度
  121. startAngle += mList.get(i).percent / total * 360f;
  122. // 缩小扇形半径
  123. k -= 0.2f;
  124. }
  125. // 中心空白:绘制圆形遮盖,颜色设置与背景色一致
  126. arcPaint.setColor(ContextCompat.getColor(getContext(), R.color.color2));
  127. canvas.drawCircle(centerX, centerY, radius_cover, arcPaint);
  128. //绘制间隔线
  129. drawLine(canvas, lineList);
  130. //绘制文字
  131. drawText(canvas);
  132. }
  133. }
  134. /**
  135. * 获取间隔线位置 [开始坐标,结束坐标]
  136. */
  137. private PointF[] getLinePointF() {
  138. // 从圆心开始
  139. float startX = centerX;
  140. float startY = centerY;
  141. // 到扇形边界结束
  142. float stopX = (float) (centerX + (radius + arcPaint.getStrokeWidth())
  143. * Math.cos(Math.toRadians(startAngle)));
  144. float stopY = (float) (centerY + (radius + arcPaint.getStrokeWidth())
  145. * Math.sin(Math.toRadians(startAngle)));
  146. PointF startPoint = new PointF(startX, startY);
  147. PointF stopPoint = new PointF(stopX, stopY);
  148. return new PointF[]{startPoint, stopPoint};
  149. }
  150. /**
  151. * 获取文本位置
  152. */
  153. private PointF getTextPointF(float k) {
  154. float textPointX = (float) (centerX + 0.6 * k * radius * Math.cos(Math.toRadians(textAngle)));
  155. float textPointY = (float) (centerY + 0.6 * k * radius * Math.sin(Math.toRadians(textAngle)));
  156. return new PointF(textPointX, textPointY);
  157. }
  158. /**
  159. * 绘制间隔线
  160. */
  161. private void drawLine(Canvas canvas, List<PointF[]> pointFs) {
  162. for (PointF[] fp : pointFs) {
  163. canvas.drawLine(fp[0].x, fp[0].y, fp[1].x, fp[1].y, linePaint);
  164. }
  165. }
  166. /**
  167. * 绘制文本
  168. */
  169. private void drawText(Canvas canvas) {
  170. for (int i = 0; i < textList.size(); i++) {
  171. textPaint.setTextAlign(Paint.Align.CENTER);
  172. // 字号
  173. textPaint.setTextSize(radius / 7);
  174. // 字体
  175. textPaint.setTypeface(Typeface.MONOSPACE);
  176. // 显示百分比
  177. canvas.drawText(format.format(mList.get(i).percent * 100 / total) + "%",
  178. textList.get(i).x - 10, textList.get(i).y - 10, textPaint);
  179. }
  180. }
  181. /**
  182. * 设置扇形和遮盖圆半径
  183. */
  184. public void setRadius(int radius, int radius_cover) {
  185. this.radius = radius;
  186. this.radius_cover = DisplayUtil.dip2px(ctx, radius_cover);
  187. }
  188. /**
  189. * 设置间隔线的颜色
  190. */
  191. public void setLineColor(int color) {
  192. linePaint.setColor(color);
  193. }
  194. /**
  195. * 设置文本颜色
  196. */
  197. public void setTextColor(int color) {
  198. textPaint.setColor(color);
  199. }
  200. /**
  201. * 设置开始角度
  202. */
  203. public void setStartAngle(float startAngle) {
  204. this.startAngle = startAngle;
  205. }
  206. /**
  207. * 设置饼的数据
  208. */
  209. public void setPieChartData(List<PieChartData> mList) {
  210. total = 0;
  211. if (mList == null) {
  212. return;
  213. }
  214. for (int i = 0; i < mList.size(); i++) {
  215. total += mList.get(i).percent;
  216. }
  217. this.mList.clear();
  218. this.mList = mList;
  219. invalidate();
  220. }
  221. }

这里还用到了一个 DisplayUtil 工具类:

  1. public class DisplayUtil {
  2. /**
  3. * dp转px
  4. */
  5. public static int dip2px(Context context, float dipValue) {
  6. final float scale = context.getResources().getDisplayMetrics().density;
  7. return (int) (dipValue * scale + 0.5f);
  8. }
  9. /**
  10. * 获取屏幕宽度和长度
  11. */
  12. public static int[] getScreenSize(Context context) {
  13. WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  14. DisplayMetrics outMetrics = new DisplayMetrics();
  15. wm.getDefaultDisplay().getMetrics(outMetrics);
  16. return new int[]{outMetrics.widthPixels, outMetrics.heightPixels};
  17. }
  18. }

最后附上源码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/254610
推荐阅读
相关标签
  

闽ICP备14008679号