赞
踩
前段时间项目里面需要用到统计图来分析数据,里面有一张饼图,博主也是第一次画饼图,一开始想偷点懒在网上找了一些资料,
结果并不是很满意,只好自己根据需求设计一张饼图了。
先看看效果图:
接下来是实现原理,这里代码里面的注释比较详尽了,就不做过多说明了。
- public class PieChartView extends View {
-
- private Context ctx;
- private DecimalFormat format;
- private List<PieChartData> mList;
-
- // 画笔
- private Paint arcPaint;
- private Paint linePaint;
- private Paint textPaint;
-
- // 圆心
- private float centerX;
- private float centerY;
- // 大圆半径
- private float radius;
- // 小圆半径
- private float radius_cover;
- private float total;
- // 初始角度
- private float startAngle;
- private float textAngle;
-
- private List<PointF[]> lineList;
- private List<PointF> textList;
-
- public PieChartView(Context context) {
- this(context, null);
- }
-
- public PieChartView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PieChartView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context);
- }
-
- private void init(Context context) {
- ctx = context;
- lineList = new ArrayList<>();
- textList = new ArrayList<>();
- mList = new ArrayList<>();
- // 取整
- format = new DecimalFormat("##0");
-
- // 扇形画笔
- arcPaint = new Paint();
- // 抗锯齿
- arcPaint.setAntiAlias(true);
- // 防抖动
- arcPaint.setDither(true);
- // 设置画笔风格,实心
- arcPaint.setStyle(Paint.Style.FILL);
-
- // 间隔线画笔
- linePaint = new Paint();
- linePaint.setAntiAlias(true);
- linePaint.setDither(true);
- linePaint.setStyle(Paint.Style.STROKE);
- linePaint.setStrokeWidth(DisplayUtil.dip2px(ctx, 2));
-
- // 文本画笔
- textPaint = new Paint();
- textPaint.setAntiAlias(true);
- textPaint.setDither(true);
- textPaint.setStyle(Paint.Style.FILL);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int width;
- int height;
- int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
-
- // 取屏幕宽度与屏幕高度的较小值
- if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.EXACTLY) {
- height = heightSpecSize;
- width = Math.min(heightSpecSize, Math.min(DisplayUtil.getScreenSize(ctx)[0],
- DisplayUtil.getScreenSize(ctx)[1]));
- } else if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.AT_MOST) {
- width = widthSpecSize;
- height = Math.min(widthSpecSize, Math.min(DisplayUtil.getScreenSize(ctx)[0],
- DisplayUtil.getScreenSize(ctx)[1]));
- } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
- width = height = Math.min(DisplayUtil.getScreenSize(ctx)[0],
- DisplayUtil.getScreenSize(ctx)[1]);
- } else {
- width = widthSpecSize;
- height = heightSpecSize;
- }
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- centerX = w / 2;
- centerY = h / 2;
- }
-
- @SuppressLint("DrawAllocation")
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- textList.clear();
- lineList.clear();
- lineList = new ArrayList<>();
- textList = new ArrayList<>();
-
- if (mList != null) {
- // 初始化缩放系数
- float k = 1.0f;
- // 开始画各个扇形
- for (int i = 0; i < mList.size(); i++) {
- // 限定饼图所在矩形大小
- float left = centerX - radius * k; // 矩形左边的X坐标
- float top = centerY - radius * k; // 矩形顶部的Y坐标
- float right = centerX + radius * k; // 矩形右边的X坐标
- float bottom = centerY + radius * k; // 矩形底部的Y坐标
- RectF mRectF = new RectF(left, top, right, bottom);
-
- // 设置扇形颜色
- arcPaint.setColor(mList.get(i).color);
- // 绘制扇形,参数:画布大小,起始角度,扇形角度,是否描边,画笔属性
- canvas.drawArc(mRectF, startAngle, mList.get(i).percent / total * 360f, true, arcPaint);
-
- // 记录间隔线位置
- lineList.add(getLinePointF());
-
- // 记录文本位置
- textAngle = startAngle + mList.get(i).percent / total * 360f / 2;
- textList.add(getTextPointF(k));
-
- //重新计算起始角度
- startAngle += mList.get(i).percent / total * 360f;
- // 缩小扇形半径
- k -= 0.2f;
- }
- // 中心空白:绘制圆形遮盖,颜色设置与背景色一致
- arcPaint.setColor(ContextCompat.getColor(getContext(), R.color.color2));
- canvas.drawCircle(centerX, centerY, radius_cover, arcPaint);
- //绘制间隔线
- drawLine(canvas, lineList);
- //绘制文字
- drawText(canvas);
- }
-
- }
-
- /**
- * 获取间隔线位置 [开始坐标,结束坐标]
- */
- private PointF[] getLinePointF() {
- // 从圆心开始
- float startX = centerX;
- float startY = centerY;
- // 到扇形边界结束
- float stopX = (float) (centerX + (radius + arcPaint.getStrokeWidth())
- * Math.cos(Math.toRadians(startAngle)));
- float stopY = (float) (centerY + (radius + arcPaint.getStrokeWidth())
- * Math.sin(Math.toRadians(startAngle)));
- PointF startPoint = new PointF(startX, startY);
- PointF stopPoint = new PointF(stopX, stopY);
- return new PointF[]{startPoint, stopPoint};
- }
-
- /**
- * 获取文本位置
- */
- private PointF getTextPointF(float k) {
- float textPointX = (float) (centerX + 0.6 * k * radius * Math.cos(Math.toRadians(textAngle)));
- float textPointY = (float) (centerY + 0.6 * k * radius * Math.sin(Math.toRadians(textAngle)));
- return new PointF(textPointX, textPointY);
- }
-
- /**
- * 绘制间隔线
- */
- private void drawLine(Canvas canvas, List<PointF[]> pointFs) {
- for (PointF[] fp : pointFs) {
- canvas.drawLine(fp[0].x, fp[0].y, fp[1].x, fp[1].y, linePaint);
- }
- }
-
- /**
- * 绘制文本
- */
- private void drawText(Canvas canvas) {
- for (int i = 0; i < textList.size(); i++) {
- textPaint.setTextAlign(Paint.Align.CENTER);
- // 字号
- textPaint.setTextSize(radius / 7);
- // 字体
- textPaint.setTypeface(Typeface.MONOSPACE);
- // 显示百分比
- canvas.drawText(format.format(mList.get(i).percent * 100 / total) + "%",
- textList.get(i).x - 10, textList.get(i).y - 10, textPaint);
- }
- }
-
- /**
- * 设置扇形和遮盖圆半径
- */
- public void setRadius(int radius, int radius_cover) {
- this.radius = radius;
- this.radius_cover = DisplayUtil.dip2px(ctx, radius_cover);
- }
-
- /**
- * 设置间隔线的颜色
- */
- public void setLineColor(int color) {
- linePaint.setColor(color);
- }
-
- /**
- * 设置文本颜色
- */
- public void setTextColor(int color) {
- textPaint.setColor(color);
- }
-
- /**
- * 设置开始角度
- */
- public void setStartAngle(float startAngle) {
- this.startAngle = startAngle;
- }
-
- /**
- * 设置饼的数据
- */
- public void setPieChartData(List<PieChartData> mList) {
- total = 0;
- if (mList == null) {
- return;
- }
- for (int i = 0; i < mList.size(); i++) {
- total += mList.get(i).percent;
- }
- this.mList.clear();
- this.mList = mList;
- invalidate();
- }
-
- }
这里还用到了一个 DisplayUtil 工具类:
- public class DisplayUtil {
-
- /**
- * dp转px
- */
- public static int dip2px(Context context, float dipValue) {
- final float scale = context.getResources().getDisplayMetrics().density;
- return (int) (dipValue * scale + 0.5f);
- }
-
- /**
- * 获取屏幕宽度和长度
- */
- public static int[] getScreenSize(Context context) {
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics outMetrics = new DisplayMetrics();
- wm.getDefaultDisplay().getMetrics(outMetrics);
- return new int[]{outMetrics.widthPixels, outMetrics.heightPixels};
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。