安卓自定义动态饼状图

最近项目需要画个动态的饼状图 旁边还有项目名称以及百分比,所以就想自己画一个,顺便练习下。先看效果图

思路:画饼状图容易,动态的饼状图就需要用到属性动画ValueAnimator,同过在动画的时候不停的画加上时间效果看上去是动态的画的一样。首先看attr文件

 <declare-styleable name="MyRoundView">
        <attr name="roundTextColor" format="color"/>
        <attr name="roundTextSize" format="dimension"/>
    </declare-styleable>

简单其实用到的只有字体大小了。

接下来就是三部曲了,onMeasure,onLayout,onDraw

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height;
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = widthSize * 1 / 2;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = heightSize * 1 / 2;
        }

        setMeasuredDimension(width, height);
    }
@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHeight = getHeight();
        mWidth = getWidth();
    }

这个主要用来定位圆的位置。主要的就是在drwa里面了看代码

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRectPaint.setColor(Color.YELLOW);
        //画的背景
//        canvas.drawRect(0, 0, getWidth(), getHeight(), mRectPaint);
//        Log.e("234","999966666==="+Thread.currentThread());
        ondraw(canvas);
        float textNameX = getHeight()/4 + circleCenterWith+dip2px(context,70);
        float textNameY = getHeight()/2-circleCenterWith;
        float textPrenX = getHeight()/4 + circleCenterWith+dip2px(context,95);
        float textPrenY = getHeight()/2-circleCenterWith;
        textPaint.setColor(roundTextColor);
        textPaint.setTextSize(roundTextSize);
        for (int i=0;i<list.size();i++){
            circlePaint.setColor(Color.parseColor(list.get(i).getColor()));
            canvas.drawCircle(textNameX,textNameY,dip2px(context,3),circlePaint);
            textPaint.getTextBounds(list.get(i).getName(),0,list.get(i).getName().length(),mBound);
            String prsent = DensityUtil.decFormatOfTwoPoint(presents.get(i)+"")+"%";
            canvas.drawText(list.get(i).getName(),textNameX+dip2px(context,20),textNameY+mBound.height()/2-dip2px(context,2),textPaint);
            canvas.drawText(prsent,textPrenX+dip2px(context,40),textPrenY+mBound.height()/2-dip2px(context,2),textPaint);
            textNameY+=dip2px(context,20);
            textPrenY+=dip2px(context,20);
        }

    }

这是画右边的名字和百分百。主要要注意位置。计算好圆的位置后就好画字的位置。看怎么画的圆弧。

/**
     * 画饼状图
     * @param canvas
     */
    private void ondraw(Canvas canvas){
        float sweepAngle = 0.0f;
//        float startAngle =0.0f;
        float startAngleY =0.0f;
        boolean isBreak=false;//退出循环
        for (int i=0;i<list.size();i++){
//            startAngle += sweepAngle;
            sweepAngle =(float) 3.6*presents.get(i);
            mPaint.setColor(Color.parseColor(list.get(i).getColor()));
            mPaint.setStyle(Paint.Style.STROKE);// 设置中空的样式
            mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
            mPaint.setStrokeWidth(dip2px(context,DEFAULT_GRENN_WIDTH));
            mRectF.set(getWidth()/4 - circleCenterWith, getHeight()/2 - circleCenterWith,
                    getWidth()/4 + circleCenterWith, getHeight()/2 + circleCenterWith);

            int leng =(int)((float)(presents.get(i)));
            float sweepAngleY = 0.0f;
            sweepAngleY = sweepAngle/leng;
            if (leng<1){
                sweepAngleY = sweepAngle;
                canvas.drawArc(mRectF, startAngleY-0.5f, sweepAngleY+0.5f, false, mPaint);
                startAngleY +=sweepAngle;//要画完再重新计算开始的角度不然会有空隙 错乱
            }
            for (int j=0;j<leng;j++){
                if (mAngle <= startAngleY)
                    isBreak = true;
                if (isBreak)
                       break;
                    canvas.drawArc(mRectF, startAngleY - 0.5f, sweepAngleY + 0.5f, false, mPaint);
                    startAngleY +=sweepAngleY;//要画完再重新计算开始的角度不然会有空隙 错乱
                }
            if (isBreak)
                break;

        }
    }

这里我先计算的是每个名字(衣服、饮食)等所占的百分比通过从属性动画里面获取的角度值和衣服饮食等所占的百分比比较小于的就画大于的就不画每次画3.6度。所占比小于百分之一的就另外画。下面放全部的代码说总没看的精彩。自己看看就明白了。

package com.py.ysl.view.myview;

import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.DecelerateInterpolator;

import com.py.ysl.R;
import com.py.ysl.bean.RoundInfo;
import com.py.ysl.utils.DensityUtil;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;


/**
 * 自定义饼状图
 */
public class RoundView extends View{



    private List<RoundInfo>list;
    private List<Float> presents;

    private Paint mPaint;
    private Paint textPaint;//字的画笔
    private Paint circlePaint;//小圆的的画笔
     private Rect mBound;
    private RectF mRectF;
    private Paint mRectPaint;

    private int roundTextColor;
    private int roundTextSize;

    private int mHeight;
    private int mWidth;
    private float circleCenterWith;
    private static final float DEFAULT_GRENN_CENYER= 55;//空白圆的半径
    private static final float DEFAULT_GRENN_WIDTH= 35;//圆弧的宽度

    private ValueAnimator cakeValueAnimator;
    private float  mAngle = 0.0f;
    private Context context;
    public RoundView(Context context){
        this(context,null);

    }
    public RoundView(Context context,AttributeSet attrs){
        this(context,attrs,0);
    }
    public RoundView(Context context,AttributeSet attrs,int defStyleAttr){
        super(context,attrs,defStyleAttr);
        this.context =context;
        initView(context,attrs,defStyleAttr);
    }
    private void initView(Context context,AttributeSet attrs,int defStyleAttr){
        TypedArray array =context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyRoundView,defStyleAttr,0);
        int count = array.getIndexCount();
        for (int i=0;i<count;i++){
            int attr = array.getIndex(i);
            switch (attr){
                case R.styleable.MyRoundView_roundTextColor:
                    roundTextColor = array.getColor(attr, Color.BLACK);
                    break;
                    case R.styleable.MyRoundView_roundTextSize:
                    roundTextSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()));
                    break;


            }
        }
        array.recycle();
        init();
    }
    private void init(){
        initValueAnimator();
        circleCenterWith =dip2px(context, DEFAULT_GRENN_CENYER);
        list = new ArrayList<>();
        presents = new ArrayList<>();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        circlePaint= new Paint();
        circlePaint.setAntiAlias(true);
        mBound = new Rect();
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        mRectF = new RectF();
        mRectPaint = new Paint();
        mRectPaint.setAntiAlias(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height;
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = widthSize * 1 / 2;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = heightSize * 1 / 2;
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHeight = getHeight();
        mWidth = getWidth();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mRectPaint.setColor(Color.YELLOW);
        //画的背景
//        canvas.drawRect(0, 0, getWidth(), getHeight(), mRectPaint);
//        Log.e("234","999966666==="+Thread.currentThread());
        ondraw(canvas);
        float textNameX = getHeight()/4 + circleCenterWith+dip2px(context,70);
        float textNameY = getHeight()/2-circleCenterWith;
        float textPrenX = getHeight()/4 + circleCenterWith+dip2px(context,95);
        float textPrenY = getHeight()/2-circleCenterWith;
        textPaint.setColor(roundTextColor);
        textPaint.setTextSize(roundTextSize);
        for (int i=0;i<list.size();i++){
            circlePaint.setColor(Color.parseColor(list.get(i).getColor()));
            canvas.drawCircle(textNameX,textNameY,dip2px(context,3),circlePaint);
            textPaint.getTextBounds(list.get(i).getName(),0,list.get(i).getName().length(),mBound);
            String prsent = DensityUtil.decFormatOfTwoPoint(presents.get(i)+"")+"%";
            canvas.drawText(list.get(i).getName(),textNameX+dip2px(context,20),textNameY+mBound.height()/2-dip2px(context,2),textPaint);
            canvas.drawText(prsent,textPrenX+dip2px(context,40),textPrenY+mBound.height()/2-dip2px(context,2),textPaint);
            textNameY+=dip2px(context,20);
            textPrenY+=dip2px(context,20);
        }

    }

    /**
     * 画饼状图
     * @param canvas
     */
    private void ondraw(Canvas canvas){
        float sweepAngle = 0.0f;
//        float startAngle =0.0f;
        float startAngleY =0.0f;
        boolean isBreak=false;//退出循环
        for (int i=0;i<list.size();i++){
//            startAngle += sweepAngle;
            sweepAngle =(float) 3.6*presents.get(i);
            mPaint.setColor(Color.parseColor(list.get(i).getColor()));
            mPaint.setStyle(Paint.Style.STROKE);// 设置中空的样式
            mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
            mPaint.setStrokeWidth(dip2px(context,DEFAULT_GRENN_WIDTH));
            mRectF.set(getWidth()/4 - circleCenterWith, getHeight()/2 - circleCenterWith,
                    getWidth()/4 + circleCenterWith, getHeight()/2 + circleCenterWith);

            int leng =(int)((float)(presents.get(i)));
            float sweepAngleY = 0.0f;
            sweepAngleY = sweepAngle/leng;
            if (leng<1){
                sweepAngleY = sweepAngle;
                canvas.drawArc(mRectF, startAngleY-0.5f, sweepAngleY+0.5f, false, mPaint);
                startAngleY +=sweepAngle;//要画完再重新计算开始的角度不然会有空隙 错乱
            }
            for (int j=0;j<leng;j++){
                if (mAngle <= startAngleY)
                    isBreak = true;
                if (isBreak)
                       break;
                    canvas.drawArc(mRectF, startAngleY - 0.5f, sweepAngleY + 0.5f, false, mPaint);
                    startAngleY +=sweepAngleY;//要画完再重新计算开始的角度不然会有空隙 错乱
                }
            if (isBreak)
                break;

        }
    }


    /**
     * 元数据
     * @param list
     */
    public   void setData(List<RoundInfo>list){
        this.list = list;
        int total = 0;
        for (int i=0;i<list.size();i++){
            total += Integer.parseInt(list.get(i).getCount());
        }
        for (int i=0;i<list.size();i++){
            float cursent  = (float) (Integer.parseInt(list.get(i).getCount()))*100/total;

            presents.add(cursent);
        }
        cakeValueAnimator.start();
        invalidate();
    }
    public int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    private void initValueAnimator() {

        PropertyValuesHolder angleValues = PropertyValuesHolder.ofFloat(
                "angle", 0f, 360f);
        cakeValueAnimator = ValueAnimator.ofPropertyValuesHolder(angleValues);
        cakeValueAnimator.setDuration(1500);
        cakeValueAnimator.setRepeatCount(0);
        cakeValueAnimator.setInterpolator(new DecelerateInterpolator());
        cakeValueAnimator.setRepeatMode(ValueAnimator.RESTART);
        updataAnimator();

    }

    /**
     * 动作效果
     * @param
     */
    private void updataAnimator(){

        cakeValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                 mAngle = (Float)animation.getAnimatedValue("angle");
//               long now = Calendar.getInstance().getTimeInMillis();
//                long vi=now-time;
//
//               time = now;

               invalidate();
            }
        });
    }
}

最后在activity里面调用随便造的数据。

RoundInfo info = new RoundInfo();
        info.setCount("250");
        info.setColor("#ffd862");
        info.setName("衣服");
        RoundInfo info2 = new RoundInfo();
        info2.setCount("1250");
        info2.setName("饮食");
        info2.setColor("#71c6fe");

        RoundInfo info3 = new RoundInfo();
        info3.setCount("2250");
        info3.setName("购物");
        info3.setColor("#5f98fe");

        RoundInfo info4 = new RoundInfo();
        info4.setCount("10");
        info4.setName("桑拿");
        info4.setColor("#fe7614");

        RoundInfo info5 = new RoundInfo();
        info5.setCount("115");
        info5.setName("淘宝");
        info5.setColor("#a488fe");

        RoundInfo info6 = new RoundInfo();
        info6.setCount("350");
        info6.setName("喝酒");
        info6.setColor("#ff9693");
        roundList.add(info);
        roundList.add(info2);
        roundList.add(info3);
        roundList.add(info4);
        roundList.add(info5);
        roundList.add(info6);
//       

        roundView.setData(roundList);

这样就完成了,大神们还有什么更简单的方法可以说下,一起学习下。感谢

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值