Android-可旋转、平移的饼状图(PieChartView)

前言

这次的饼图和之前写过的都不太一样。主要是饼图的旋转是通过Button去触发,被选中的某块需要平移出来。

好了,先看一下效果图


旋转的卡顿是模拟机的原因,真机是没问题的♪(*)

自定义View

public class AnimatePieChartView extends View {

    public static final String TAG = "ez";
    //默认起始的旋转角度
    private final float DEFAULT_START_ANGLE = 180;
    //某块饼图平移的距离
    public static final int TRANS_DIS = -20;
    private Paint mPaintOuter;
    private Paint mPaintCenter;
    private Paint mPaintShadow;

    private int mPaddingTop;
    private int mPaddingBottom;
    private int mPaddingStart;
    private int mPaddingEnd;

    //圆心
    private int mCenterX;
    private int mCenterY;

    //开始角度
    private float mStartAngle;
    private float mMaxValue;
    //文字大小
    private float mTextSize;
    //阴影大小
    private float mShaderSize;
    //当前颜色
    private int mCurrentColor;
    //当前选中的index
    private int mIndex;
    //颜色
    private List<Integer> mPieColorList;
    //颜色占比
    private List<Float> mPieValueList;
    //饼图文字
    private List<String> mPieStringList;
    //选中角度
    private List<Float> angleList;
    //半径
    private int mRadius;
    //中心圆半径
    private int circleRadius;
    //中心圆文字
    private String text;
    //中心圆颜色
    private int centerColor;

    public AnimatePieChartView(Context context) {
        super(context, null);
    }

    public AnimatePieChartView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AnimatePieChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //自定义一些属性
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PieChartView);

        mRadius = a.getDimensionPixelSize(R.styleable.PieChartView_pie_radius,
                getResources().getDimensionPixelSize(R.dimen.pie_default_radius));
        circleRadius = a.getDimensionPixelSize(R.styleable.PieChartView_centerCircle_radius,
                getResources().getDimensionPixelSize(R.dimen.pie_center_radius));
        mTextSize = a.getDimension(R.styleable.PieChartView_textSize,
                getResources().getDimension(R.dimen.pie_text_size));
        mShaderSize = a.getDimension(R.styleable.PieChartView_shaderSize,
                getResources().getDimension(R.dimen.pie_shader_size));

        centerColor = getResources().getColor(R.color.color_window_background);

        a.recycle();

        mPaddingTop = getPaddingTop();
        mPaddingBottom = getPaddingBottom();
        mPaddingStart = getPaddingLeft();
        mPaddingEnd = getPaddingRight();

        initPaint();
    }

    /**
     * 初始化Paint
     */
    private void initPaint() {
        mPieColorList = new ArrayList<>();
        mPieValueList = new ArrayList<>();
        mPieStringList = new ArrayList<>();
        angleList = new ArrayList<>();


        mPaintOuter = new Paint();
        mPaintOuter.setStyle(Paint.Style.FILL);
        mPaintOuter.setAntiAlias(true);

        mPaintCenter = new Paint();
        mPaintCenter.setColor(centerColor);
        mPaintCenter.setStyle(Paint.Style.FILL);
        mPaintCenter.setAntiAlias(true);
        mPaintCenter.setTextSize(mTextSize);
        mPaintCenter.setTextAlign(Paint.Align.CENTER);

        mPaintShadow = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintShadow.setAntiAlias(true);
    }

    /**
     * 获取饼图每一部分的旋转角度
     */
    private float getRotationAngle(int i) {
        float angleR;
        float angleT = angleList.get(i);
        if (angleT <= 270f && angleT >= 90f) {
            angleR = 90f - angleT;
        } else if (angleT > 270f && angleT <= 360f) {
            angleR = 360f - angleT + 90f;
        } else if (angleT >= 0 && angleT < 90) {
            angleR = 90 - angleT;
        } else {
            angleR = 0;
        }

        for (int id = 0; id < angleList.size(); id++) {
            float temp = angleList.get(id) + angleR;
            if (temp > 360f) {
                temp -= 360f;
            } else if (temp < 0) {
                temp += 360f;
            }
            angleList.set(id, temp);
        }
        return angleR;
    }

    //测量View的宽高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int mWidth = mRadius * 2 + mPaddingStart + mPaddingEnd;
        int mHeight = mRadius * 2 + mPaddingTop + mPaddingBottom;

        if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(mWidth, mHeight);
        } else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(mWidth, heightSize);
        } else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(widthSize, mHeight);
        }
    }
    
    //size改变后,重新测量View
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCenterX = mPaddingStart + (w - mPaddingStart - mPaddingEnd) / 2;
        mCenterY = mPaddingTop + (h - mPaddingTop - mPaddingBottom) / 2;
        mPaintShadow.setShader(new RadialGradient(mCenterX, mCenterY,
                circleRadius + mShaderSize,
                Color.TRANSPARENT, Color.TRANSPARENT, Shader.TileMode.CLAMP));
    }

    /**
     * 旋转前的饼图
     */
    private void drawPie(Canvas canvas, float amount,int i) {
        mPaintOuter.setColor(mCurrentColor);
        float mAngle = 360  * amount / mMaxValue;
        RectF oval = new RectF(mCenterX - mRadius, mCenterY - mRadius,mCenterX + mRadius,mCenterY + mRadius);
        canvas.drawArc(oval, mStartAngle, mAngle, true, mPaintOuter);
        mStartAngle += mAngle;
    }

    /**
     * 旋转后的饼图
     */
    private void drawPieTouch(Canvas canvas, float amount,int i) {
        mPaintOuter.setColor(mCurrentColor);
        float mAngle = 360  * amount / mMaxValue;
        float mRadiusTemp = mRadius ;
        RectF oval = new RectF(mCenterX - mRadiusTemp, mCenterY - mRadiusTemp,mCenterX + mRadiusTemp,mCenterY + mRadiusTemp);
        canvas.drawArc(oval, mStartAngle + mAngle , mAngle - mAngle  * 2, true, mPaintOuter);
        mStartAngle += mAngle;
        canvas.drawText(mPieStringList.get(i),getMeasuredWidth()/2,getMeasuredHeight()/4,mPaintCenter);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mPieValueList.size(); i++) {
            mCurrentColor = mPieColorList.get(i);
            if (i == mIndex) {
                canvas.save();
                canvas.translate(0,TRANS_DIS);
                drawPieTouch(canvas, mPieValueList.get(i),i);
                canvas.restore();
            } else {
                drawPie(canvas, mPieValueList.get(i),i);
            }
        }
        canvas.drawCircle(mCenterX, mCenterY,
                circleRadius + mShaderSize, mPaintShadow);
        mPaintCenter.setColor(centerColor);
    }

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

    /**
     *
     * 旋转选中的某一项
     */
    public void onAnimatedPie(int i) {
        mIndex = i;
        float angle = getRotationAngle(i);
        ValueAnimator animatorRotation;
        animatorRotation = ValueAnimator.ofFloat(mStartAngle, mStartAngle + angle);
        animatorRotation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mStartAngle = (Float) animation.getAnimatedValue();
                invalidate();
            }
        });

        int time = (int) (1000 * Math.abs(angle) / 360);

        animatorRotation.setDuration(time);
        animatorRotation.start();
    }

    /**
     * 饼图的各个属性
     *
     */
    public void setPie(List<PieData> pieList) {
        mMaxValue = 0;
        mPieColorList = new ArrayList<>();
        mPieStringList = new ArrayList<>();
        mPieValueList = new ArrayList<>();
        angleList = new ArrayList<>();

        for (PieData pie : pieList) {
            mPieColorList.add(pie.pieColor);
            mPieValueList.add(pie.pieValue);
            mMaxValue += pie.pieValue;
            mPieStringList.add(pie.pieString);
        }

        float angleTemp;
        float startAngleTemp = DEFAULT_START_ANGLE;

        for (float v : mPieValueList) {
            angleTemp = 360 * v / mMaxValue;

            angleList.add(startAngleTemp + angleTemp / 2);

            startAngleTemp += angleTemp;
        }
    }

}

调用方式

    //每个饼图的占比
    private float[] mPies;
    //饼图
    private AnimatePieChartView mAnimatePieChartView;
    //初始化当前的index
    private int mIndex=0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_animate_pie_view_layout);
        findView();
        mAnimatePieChartView = (AnimatePieChartView) findViewById(R.id.id_animate_pie_view);
        setPieData(mAnimatePieChartView);
    }
    //findViewById
    private void findView(){
        Button btnPre= (Button) findViewById(R.id.id_btn_pre);
        Button btnNext= (Button) findViewById(R.id.id_btn_next);

        btnPre.setOnClickListener(this);
        btnNext.setOnClickListener(this);
    }
    //初始化饼图属性
    private void setPieData(AnimatePieChartView animatePieChartView) {
        final TypedArray typedArray = getResources().obtainTypedArray(R.array.ring_colors);
        final int size = 5;
        final int length = typedArray.length();
        //饼图数据集合
        List<PieData> pieEntryList = new ArrayList<>();
        mPies = new float[]{20f, 30f, 10f, 50f, 15f};
        String[] positions=new String[]{"上单","打野","中单","下路","辅助"};
        int color ;
        for(int i = 0; i < size; i++) {
            //饼图每块的颜色
            if(i >= length) {
                color = typedArray.getColor(length - 1, 0);
            } else {
                color = typedArray.getColor(i, 0);
            }
            //每块饼图所需要的数据
            PieData pe = new PieData(mPies[i],positions[i] , color);
            
            pieEntryList.add(pe);
        }
        animatePieChartView.setPie(pieEntryList);
        if(size > 1) {
            animatePieChartView.onTouchPie(0);
        } else {
            animatePieChartView.invalidate();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.id_btn_pre:
                if (mIndex <= 0) {
                    mIndex = mPies.length -1;
                } else {
                    mIndex = mIndex - 1;
                }
                animated(mIndex);
                break;
            case R.id.id_btn_next:
                if (mIndex >= mPies.length - 1) {
                    mIndex = 0;
                } else {
                    mIndex = mIndex + 1;
                }
                animated(mIndex);
                break;
        }
    }
    //旋转
    private void animated(int index) {
        if(mPies.length > 1) {
            mAnimatePieChartView.onAnimatedPie(index);
        }
    }

具体的实现和调用方式就这么多了。

自定义的属性和代码可查看具体代码地址。
代码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容