自定义控件实现——环形饼图
来源:互联网 发布:matlab dsp编程 编辑:程序博客网 时间:2024/06/06 16:51
1 需求:
1) 根据不同份额占用等比例的份额,用不同颜色表示
2) 饼图要求:环形;有阴影效果;不同份额颜色不同;从外到内颜色不同
3) 加载数据有动画效果
2 原理
1)封装Bean:表示的值,开始颜色,结束颜色;
2)drawArc +paint宽度设置为圆环宽度;
3)颜色渐变:
RadialGradient gradient = new RadialGradient(centerX, centerY, radius,
new int[]{Color.TRANSPARENT, Color.TRANSPARENT, arcs.get(i).startColor, arcs.get(i).endColor},
new float[]{0, innerRadius / outerRadius, innerRadius / outerRadius, 1},
Shader.TileMode.CLAMP);
paint.setShader(gradient);//RadialGradient
3)阴影效果:
Paint.setShadowLayer(shadowRadius / 2, 0, shadowRadius / 4, 0xFFDF4242);
4)动画效果:
记录一个开始时间startTime,动画要执行时间为固定 值;
System.currentTimeMillis() 不断和startTime比较,到达固定值,结束动画
进度:时间比例计算
实现:onDraw中判断时间未到的话直接invalidate();
3 代码实现
**package com.bpj.piechart;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.RadialGradient;import android.graphics.RectF;import android.graphics.Shader;import android.util.AttributeSet;import android.util.Log;import android.view.View;import java.util.ArrayList;import java.util.List;/** * 作者 chenli * 日期 2017/8/24 * 描述 饼图 **/public class PieChartView extends View { private static float ANGLE_ROUND = 360f; private static float ANGLE_START = -90f; private static int TIME_ANIM = 800; // 可封装为attrs private float outerRadius; private float innerRadius; private float shadowRadius; private int count; private float[] startAngles; private float[] sweepAngles; private ArcRing[] arcRings; private RectF rect; // 圆环内外环1/2处的圆所在区域 private float radius; // 圆环内外环1/2处的半径 private float width; // 圆环内外环之间的宽度 private float centerX; // 圆环圆心x private float centerY; // 圆环圆心y private Paint paint; private long timeStart; private boolean set; public PieChartView(Context context) { super(context); init(); } public PieChartView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public PieChartView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setLayerType(View.LAYER_TYPE_SOFTWARE, null); outerRadius = dip2px(getContext(), 55); // 55dp innerRadius = dip2px(getContext(), 32); // 32dp shadowRadius = dip2px(getContext(), 10); // 10dp width = outerRadius - innerRadius; radius = outerRadius - width / 2; centerX = outerRadius + shadowRadius; centerY = outerRadius + shadowRadius; rect = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius); // init paint paint = new Paint(); paint.setColor(0xFFDF4242); paint.setShadowLayer(shadowRadius / 2, 0, shadowRadius / 4, 0xFFDF4242); paint.setStrokeWidth(width - 1); paint.setStyle(Paint.Style.STROKE); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width; int height; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { width = (int) (getPaddingLeft() + outerRadius * 2 + shadowRadius * 2 + getPaddingRight()); } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { height = (int) (getPaddingTop() + outerRadius * 2 + shadowRadius * 2 + getPaddingBottom()); } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (count == 0) { return; } long timeCurrent = System.currentTimeMillis(); if (timeCurrent - this.timeStart < TIME_ANIM) { float angle = (timeCurrent - this.timeStart) * ANGLE_ROUND / TIME_ANIM; canvas.drawArc(rect, ANGLE_START, angle, false, paint); for (int i = 0; i < count; i++) { arcRings[i].draw(canvas, angle); } invalidate(); } else { canvas.drawArc(rect, ANGLE_START, ANGLE_ROUND, false, paint); for (int i = 0; i < count; i++) { arcRings[i].draw(canvas); } } } private static class ArcRing { Paint paint; RectF rect; float startAngle; float sweepAngle; ArcRing(float width, Shader gradient, RectF rect, float startAngle, float sweepAngle) { this.rect = rect; this.startAngle = startAngle; this.sweepAngle = sweepAngle; paint = new Paint(); paint.setAntiAlias(true); paint.setShader(gradient); paint.setStrokeWidth(width); paint.setStyle(Paint.Style.STROKE); } void draw(Canvas canvas) { canvas.drawArc(rect, startAngle, sweepAngle, false, paint); } void draw(Canvas canvas, float angle) { if (angle >= startAngle - ANGLE_START) { if (angle <= startAngle - ANGLE_START + sweepAngle) { canvas.drawArc(rect, startAngle, angle + ANGLE_START - startAngle, false, paint); } else { canvas.drawArc(rect, startAngle, sweepAngle, false, paint); } } } } public void update(List<Arc> arcs) { if (arcs == null || arcs.isEmpty()) { clear(); return; } this.timeStart = System.currentTimeMillis(); count = arcs.size(); startAngles = new float[count]; sweepAngles = new float[count]; arcRings = new ArcRing[count]; float totalProperties = 0f; for (Arc arc : arcs) { totalProperties += Math.abs(arc.value); } float usedSweepAngle = 0; for (int i = 0; i < count; i++) { if (i == count - 1) { sweepAngles[i] = ANGLE_ROUND - usedSweepAngle; } else { if (totalProperties < 0.01) { sweepAngles[i] = ANGLE_ROUND / count; } else { sweepAngles[i] = Math.abs(arcs.get(i).value) * ANGLE_ROUND / totalProperties; } } if (i == 0) { startAngles[i] = ANGLE_START; } else { startAngles[i] = startAngles[i - 1] + sweepAngles[i - 1]; } RadialGradient gradient = new RadialGradient(centerX, centerY, radius, new int[]{Color.TRANSPARENT, Color.TRANSPARENT, arcs.get(i).startColor, arcs.get(i).endColor}, new float[]{0, innerRadius / outerRadius, innerRadius / outerRadius, 1}, Shader.TileMode.CLAMP); arcRings[i] = new ArcRing(width, gradient, rect, startAngles[i], sweepAngles[i]); usedSweepAngle += sweepAngles[i]; } invalidate(); } public void clear() { count = 0; startAngles = null; sweepAngles = null; arcRings = null; invalidate(); } public List<Float> getPercentages() { List<Float> floats = new ArrayList<>(); if (sweepAngles != null) { for (int i = 0; i < sweepAngles.length; i++) { floats.add(sweepAngles[i] / ANGLE_ROUND); } } return floats; } public static class Arc { float value; int startColor; int endColor; public Arc(float value, int startColor, int endColor) { this.value = value; this.startColor = startColor; this.endColor = endColor; } } public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}**
调用:
PieChartView.Arc arc1 = new PieChartView.Arc(10,0xFFFA5539,0xFFFA3252); PieChartView.Arc arc2 = new PieChartView.Arc(20,0xFFFCCB3C,0xFFF78E26); PieChartView.Arc arc3 = new PieChartView.Arc(30,0xFF8969FF,0xFF157EFB); PieChartView.Arc arc4 = new PieChartView.Arc(40,0xFF17EAD9,0xFF60B3EA); PieChartView.Arc arc5 = new PieChartView.Arc(40,0xFFDAAB66,0xFFB18545); List<PieChartView.Arc> list = new ArrayList<>(); list.add(arc1); list.add(arc2); list.add(arc3); list.add(arc4); list.add(arc5); pcv.update(list);
阅读全文