SeniorUI0304_环形饼图

高级UI汇总​​​​​​​
这里写图片描述

#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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值