#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);