扇形图(饼状图)的实现

废话不多说,先直接上最终的效果图(完整代码在底部)
扇形图(饼状图)的实现_第1张图片
最终图.png

我把这个view拆分理解,主要的思路如下:
1.通过 canvas.drawArc()方法画出单个扇形,再根据起始角度和偏移角度不断的添加扇形,最终实现整个一个圆形。
2.添加动画效果,使得圆形的展开是一个渐进的过程,不是一下子就展现。
3.填上文字
上面3点就是主要的思路。
有了思路,就动手撸代码了,。

 RectF rectf = new RectF(-100, -100, 100, 100);
 canvas.drawArc(rectf, 0,36, true, mPaint); //画扇形

画完整的圆形的重点在于上面一段代码,理解了这段代码就好操作多了。上述代码得到的效果如下
扇形图(饼状图)的实现_第2张图片
画扇形.png

Step 1. 画完整的圆形 (需要先将canvas的原点,移到屏幕中心)

  if (null == mDatas) {
            return;
        }
        float currentStartAngle = mStartAngle; //当前起始角度
        canvas.translate(mWith, mHight); //移到中心位置
        mRadius = (float) (Math.min(mWith, mHight) * 0.8); //设定圆的半径大小

        RectF rectf = new RectF(-mRadius, -mRadius, mRadius, mRadius);
        for (int i = 0; i < mDatas.size(); i++) { //通过循环,不断的拼接扇形
            canvas.drawArc(rectf, currentStartAngle, mDatas.get(i).getAngle(), true, mPaint);       //画扇形
            mPaint.setColor(colors[i % colors.length]);
            currentStartAngle += mDatas.get(i).getAngle();   //下一个的扇形起始角度
        }

代码跑完的效果如下:


扇形图(饼状图)的实现_第3张图片
饼状图.png

Step 2. 添加动画效果

首先我们需要定义一下动画的效果,动画完成的时间为3秒,并且是线性的

    /**
     * 添加动画效果
     */
    public void initAnimation() {
        ValueAnimator anim = ValueAnimator.ofFloat(0, 360);
        anim.setDuration(3000);
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                animatedValue = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        anim.start();
    }

定义好了动画效果之后,我们要在画扇形部分的代码做些稍微的变动。也就是第一步的画扇形的代码部分

    if (animatedValue - currentStartAngle > 0) {
                canvas.drawArc(rectf, currentStartAngle, animatedValue - currentStartAngle, true, mPaint);
                drawText(canvas, currentStartAngle + mDatas.get(i).getAngle() / 2, mDatas.get(i));
            }

这样动画的效果就可以了(没有做成gif图片,所以就将就的看着.)
扇形图(饼状图)的实现_第4张图片
添加动画.png

Step 3. 添加文字

添加文字,我这里偷了下懒,直接通过canvas.drawTextOnPath()方法来实现,这样做的最终结果就是画上去的字是围着圆绕的,并不是端正的。如果有这个需求,可以通过canvas.drawText()方法来实现,就比如圆中心的hello字符串就是通过如下代码

        canvas.drawText("hello", 0, 0, textPain);

整个添加文字的代码如下,在setp1的for循环里面调用即可。

 /**
     * 在当前要画文字的饼图中间文字
     *
     * @param starAngle 起始角度
     * @param pieData   当前要画文字的饼图数据
     */

    public void drawText(Canvas canvas, float starAngle, PieData pieData) {

        Path path = new Path();
        path.addArc(new RectF(-mRadius, -mRadius, mRadius, mRadius), starAngle, pieData.getAngle());
        canvas.drawTextOnPath(pieData.getName() + " /" + (int) (pieData.getPercentage() * 100) + "%", path, -10, -10, textPain);
        canvas.drawText("hello", 0, 0, textPain); //在圆点写文字
    }

最终的效果就完成了,效果参见首张图,接下来附上完整的代码:

package com.example.five.demotest.views;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

import com.example.five.demotest.bean.PieData;

import java.util.ArrayList;

/**
 * 饼状图
 */

public class PieView extends View {

    private int[] colors = {Color.RED, Color.YELLOW, Color.GREEN, Color.GRAY}; //初始化颜色

    private float mStartAngle = 0; //初始化绘制角度

    private ArrayList mDatas = new ArrayList<>(); //数据源

    private int mWith, mHight; //宽和高

    private Paint mPaint = new Paint(); //初始化画笔
    private Context mContext;
    private float animatedValue = 0;
    private float mRadius; //圆的半径
    private Paint textPain = new Paint();

    public PieView(Context context) {
        this(context, null);
    }

    public PieView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PieView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        initView();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public PieView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    /**
     * 初始化控件
     */

    private void initView() {
        mPaint.setStyle(Paint.Style.FILL); //画笔设置为填充
        mPaint.setAntiAlias(true); //设置为抗锯齿

        textPain.setStyle(Paint.Style.FILL); //画笔设置为填充
        textPain.setAntiAlias(true); //设置为抗锯齿
        textPain.setTextSize(20);

        initAnimation();

    }

    /**
     * 绘画方法
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (null == mDatas) {
            return;
        }
        float currentStartAngle = mStartAngle; //当前起始角度
        canvas.translate(mWith, mHight); //移到中心位置
        mRadius = (float) (Math.min(mWith, mHight) * 0.8); //设定圆的半径大小

        RectF rectf = new RectF(-mRadius, -mRadius, mRadius, mRadius);
        for (int i = 0; i < mDatas.size(); i++) {
            if (animatedValue - currentStartAngle > 0) {
                canvas.drawArc(rectf, currentStartAngle, animatedValue - currentStartAngle, true, mPaint);
                drawText(canvas, currentStartAngle + mDatas.get(i).getAngle() / 2, mDatas.get(i));
            }
            mPaint.setColor(colors[i % colors.length]);

            currentStartAngle += mDatas.get(i).getAngle();
        }

    }


    /**
     * 在当前要画文字的饼图中间文字
     *
     * @param starAngle 起始角度
     * @param pieData   当前要画文字的饼图数据
     */

    public void drawText(Canvas canvas, float starAngle, PieData pieData) {

        Path path = new Path();
        path.addArc(new RectF(-mRadius, -mRadius, mRadius, mRadius), starAngle, pieData.getAngle());
        canvas.drawTextOnPath(pieData.getName() + " /" + (int) (pieData.getPercentage() * 100) + "%", path, -10, -10, textPain);
        canvas.drawText("hello", 0, 0, textPain);
    }

    /**
     * 添加动画效果
     */
    public void initAnimation() {
        ValueAnimator anim = ValueAnimator.ofFloat(0, 360);
        anim.setDuration(3000);
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                animatedValue = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        anim.start();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHight = h / 2;
        mWith = w / 2;
    }

    /**
     * 设置起始位置
     *
     * @param startAngle
     */
    public void setmStartAngle(int startAngle) {
        this.mStartAngle = startAngle;
        invalidate(); //刷新视图
    }

    /**
     * 设置数据
     *
     * @param datas
     */
    public void setmDatas(ArrayList datas) {
        this.mDatas.clear();
        this.mDatas.addAll(datas);
        initDatas();
        invalidate();
    }

    private void initDatas() {
        if (mDatas == null || mDatas.size() == 0)
            return;
        float sumValue = 0;
        for (int i = 0; i < mDatas.size(); i++) {  //获取总的数据和
            PieData pieData = mDatas.get(i);
            sumValue += pieData.getValue();
            int j = i % colors.length;
            mDatas.get(i).setColor(colors[j]);  //更改数据值的颜色
        }

        //设置每个扇形的角度值
        for (int i = 0; i < mDatas.size(); i++) {
            PieData pieData = mDatas.get(i);
            mDatas.get(i).setPercentage(pieData.getValue() / sumValue);
            mDatas.get(i).setAngle(pieData.getValue() / sumValue * 360);
        }
    }

}

pie的数据实体类代码如下:

package com.example.five.demotest.bean;

/**
 * 饼状图的javabean
 */

public class PieData {

    private String name;//名称
    private float value;//数值
    private float percentage; //百分比

    private int color; //颜色
    private float angle; //角度

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getValue() {
        return value;
    }

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

    public float getPercentage() {
        return percentage;
    }

    public void setPercentage(float percentage) {
        this.percentage = percentage;
    }

    public int getColor() {
        return color;
    }

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

    public float getAngle() {
        return angle;
    }

    public void setAngle(float angle) {
        this.angle = angle;
    }

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

结束,敬礼!

你可能感兴趣的:(扇形图(饼状图)的实现)