android 自定义View 实现饼图 统计图形 百分比饼图显示内容 扇形图统计

最近有朋友项目需要做饼图扇形图显示的内容,就简单做了一个demo

效果如下!这里写图片描述
代码下载地址:
https://download.csdn.net/download/qq_38355313/10294797

首先自定义了一个类

package com.nxm.wordrecognition.nxm;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

import java.util.List;

/**
 * Created by LK on 2018/3/15.
 *
 * @author LK
 */

public class PieChart extends View {
    /**
     * 画笔
     */
    private Paint mPaint;
    private Paint mTvPaint;
    /**
     * 饼状图宽高
     */
    private int mWidth, mHeight;
    /**
     * 饼状图起始角度
     */
    private float mStartAngle = 0f;
    /**
     * 动画时间
     */
    private static final long ANIMATION_DURATION = 1000;
    /**
     * 用户数据
     */
    private List<PieData> mData;
    /**
     * 绘制方式
     */
    private int mDrawWay = PART;
    public static final int PART = 0;//分布绘制
    public static final int COUNT = 1;//连续绘制
    private int centerX, centerY;//中心坐标
    private int offset = 6;
    /**
     * 自定义动画
     */
    private PieChartAnimation mAnimation;
    private float sumValue = 0;//数据值的总和

    public PieChart(Context context) {
        super(context);
        init();
    }

    public PieChart(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setDither(true);//防止抖动
        mPaint.setStyle(Paint.Style.FILL);//画笔为填充
        mTvPaint = new Paint();
        mTvPaint.setStyle(Paint.Style.FILL);
        mTvPaint.setColor(Color.GRAY);
        mTvPaint.setTextSize(30);
        //初始化动画
        mAnimation = new PieChartAnimation();
        mAnimation.setDuration(ANIMATION_DURATION);
    }

    /**
     * 设置起始角度
     *
     * @param mStartAngle
     */
    public PieChart setmStartAngle(float mStartAngle) {
        this.mStartAngle = mStartAngle;
        invalidate();//刷新
        return this;
    }

    /**
     * 设置数据
     *
     * @param mData
     */
    public void setData(List<PieData> mData) {
        setmData(mData);
    }

    /**
     * 设置数据和绘制方式
     *
     * @param mData
     */
    public void setData(List<PieData> mData, int mDrawWay) {
        setmData(mData);
        this.mDrawWay = mDrawWay;
    }

    /**
     * 设置数据
     *
     * @param mData
     */
    private void setmData(List<PieData> mData) {
        sumValue = 0;
        this.mData = mData;
        initData(mData);
        startAnimation(mAnimation);
        invalidate();
    }

    /**
     * 总和
     *
     * @param sumValue
     */
    public PieChart setSumValue(float sumValue) {
        this.sumValue = sumValue;
        invalidate();//刷新
        return this;
    }

    /**
     * 初始化数据
     *
     * @param mData
     */
    private void initData(List<PieData> mData) {
        if (mData == null || mData.size() == 0) {
            return;
        }
        /**
         * 计算数据总和确定颜色
         */
        for (int i = 0; i < mData.size(); i++) {
            PieData data = mData.get(i);
            sumValue += data.getValue();
            data.setColor(COLORS[i]);
        }
        /**
         * 计算百分比和角度
         */
        float currentStartAngle = mStartAngle;
        for (int i = 0; i < mData.size(); i++) {
            PieData data = mData.get(i);
            data.setCurrentStartAngle(currentStartAngle);
            //通过总和来计算百分比
            float percentage = data.getValue() / sumValue;
            //通过百分比来计算对应的角度
            float angle = percentage * 360;
            //设置用户数据
            data.setPercentage(percentage);
            data.setAngle(angle);
            currentStartAngle += angle;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mData == null) {
            return;
        }
        centerX = (getRight() - getLeft()) / 2;
        centerY = (getBottom() - getTop()) / 2;
        //1.移动画布到中心点
        canvas.translate(mWidth / 2, mHeight / 2);
        //2.设置当前起始角度
        float currentStartAngle = mStartAngle;
        //3.确定饼图的半径
        float r = (float) (Math.min(mWidth, mHeight) / 3);
        float r1 = r / 2;
        float r2 = r / 1.8f;
        float stopX = 0;
        float stopY = 0;
        //4.确定饼图的矩形大小
        RectF rectF = new RectF(-r, -r, r, r);
        RectF rectF1 = new RectF(-r1, -r1, r1, r1);
        RectF rectF2 = new RectF(-r2, -r2, r2, r2);
        for (int i = 0; i < mData.size(); i++) {
            PieData data = mData.get(i);
            //5.设置颜色
            mPaint.setColor(data.getColor());
            //6.绘制饼图

            canvas.drawArc(rectF, currentStartAngle, data.getAngle(), true, mPaint);
            //7.绘制下一块扇形时先将角度加上当前扇形的角度
            mPaint.setColor(data.getColor());
            stopX = (float) ((r + 30) * Math.cos((2 * currentStartAngle + data.getAngle()) / 2 * Math.PI / 180));
            stopY = (float) ((r + 30) * Math.sin((2 * currentStartAngle + data.getAngle()) / 2 * Math.PI / 180));
            //canvas.drawPoint(stopX, stopY, TPaint);
            canvas.drawCircle(stopX, stopY, 10, mPaint);
            currentStartAngle += data.getAngle();

            //画横线
            int dx;
            // 判断横线是画在左边还是右边
            int endX;
            if (stopX > 0) {
                endX = (centerX - getPaddingRight() - 20);
            } else {
                endX = (-centerX + getPaddingLeft() + 20);
            }
            //画横线
            canvas.drawLine(stopX, stopY,
                    endX, stopY, mPaint);
            dx = (int) (endX - stopX);
            //测量文字大小
            Rect rect = new Rect();
            String text = data.getText();
            mTvPaint.getTextBounds(text, 0, text.length(), rect);
            int w = rect.width();
            int h = rect.height();
            canvas.drawText(text, 0, text.length(), dx > 0 ? endX - offset - w : endX + offset, stopY + h + offset, mTvPaint);
            Rect rect2 = new Rect();
            String value = data.getPercentage();
            mTvPaint.getTextBounds(value, 0, value.length(), rect2);
            int w2 = rect2.width();
            int h2 = rect2.height();
            canvas.drawText(value, 0, value.length(), dx > 0 ? endX - offset - w2 : endX + offset, stopY - offset, mTvPaint);
        }
        //绘制中心空白处
        mPaint.setColor(0x11000000);
        canvas.drawArc(rectF2, currentStartAngle, 360f, true, mPaint);
        //绘制中心阴影部分
        mPaint.setColor(Color.parseColor("#FFFFFF"));
        canvas.drawArc(rectF1, currentStartAngle, 360f, true, mPaint);
        //绘制文字
        mPaint.setColor(0xEEFF4567);
        mPaint.setTextSize(80);
//        Rect textRect = new Rect();
//        mPaint.getTextBounds(str, 0, str.length(), textRect);
        // canvas.drawText("", -textRect.width() / 2, 0, mPaint);
    }


    /**
     * 自定义动画
     */
    public class PieChartAnimation extends Animation {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            if (interpolatedTime < 1.0f) {
                for (int i = 0; i < mData.size(); i++) {
                    PieData data = mData.get(i);
                    //通过总和来计算百分比
                    float percentage = data.getValue() / sumValue;
                    //通过百分比来计算对应的角度
                    float angle = percentage * 360;
                    //根据插入时间来计算角度
                    angle = angle * interpolatedTime;
                    data.setAngle(angle);
                }
            } else {//默认显示效果
                for (int i = 0; i < mData.size(); i++) {
                    //通过总和来计算百分比
                    PieData data = mData.get(i);
                    float percentage = data.getValue() / sumValue;
                    //通过百分比来计算对应的角度
                    float angle = percentage * 360;
                    data.setPercentage(percentage);
                    data.setAngle(angle);
                }
            }
            invalidate();
        }
    }

    public final static int[] COLORS = {
            Color.rgb(192, 255, 140), Color.rgb(140, 234, 255)
            , Color.rgb(255, 247, 140), Color.rgb(255, 208, 140)
            , Color.rgb(140, 234, 255), Color.rgb(255, 140, 157)
            , Color.rgb(45, 192, 252), Color.rgb(53, 194, 209)
            , Color.rgb(52, 152, 219), Color.rgb(255, 255, 240)};
}

其次定义一个数据模型bean

package com.nxm.wordrecognition.nxm;

import android.util.Log;

import java.text.DecimalFormat;

/**
 * Created by LK on 2018/3/15.
 *
 * @author LK
 */

public class PieData {
    private String text;
    private float value;
    private float Angle;
    private float Percentage;
    private int color;
    private float CurrentStartAngle;

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public float getCurrentStartAngle() {
        return CurrentStartAngle;
    }

    public void setCurrentStartAngle(float currentStartAngle) {
        CurrentStartAngle = currentStartAngle;
    }

    public String getPercentage() {
        DecimalFormat decimalFormat = new DecimalFormat("00.0");
        Log.e("Lzx", Percentage + "");
        return decimalFormat.format(Percentage*100) + "%";
    }


    public void setPercentage(float percentage) {
        //保留两位小数点
        Percentage = percentage;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public float getValue() {
        return value;
    }

    public void setValue(float value) {
        this.value = value;
    }

    public float getAngle() {
        return Angle;
    }

    public void setAngle(float angle) {
        Angle = angle;
    }

    public PieData() {
    }

    public PieData(float value) {
        this.value = value;
    }

    public PieData(String text, float value) {
        this.text = text;
        this.value = value;
    }
}

然后即可在layout中插入该view使用了

public class MainActivity extends AppCompatActivity {
    private PieChart mPieChart;
    private List<PieData> pieData;
    public static final int COUNT = 1;//连续绘制

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mPieChart = f(R.id.mPieChart);
        pieData = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            pieData.add(new PieData("text" + i, (float) Math.random() * 90));
        }
        mPieChart.setData(pieData, COUNT);
    }

    public <T extends View> T f(int id) {
        return (T) findViewById(id);
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值