天天看點

android 自定義百分比餅圖

先上圖

普通效果

android 自定義百分比餅圖

點選效果,點選之後會放大半徑

android 自定義百分比餅圖

實作百分比圓餅,整體步驟分為

1.先根據資料集占的百分比畫圓弧,使用不同顔色,很簡單

2.然後設定點選重繪圓餅,判斷點選區域在不在圓上,如果在圓上,那麼在具體的哪個圓弧上面

上代碼:直接拿去使用即可

package com.cnki.roundcake;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RegionIterator;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

/**
 * 百分比圓餅圖
 * Created by liweidong on 2019/12/2.
 */

public class RoundCake extends View{

    //資料集
    private ArrayList<CakeBean> mCakeBean;
    private Paint paint;
    //半徑
    private int radius = 150;
    private RectF rectF;
    private RectF rectFTouch;
    //畫百分比時每次的開始角度
    private float startRadius;
    private float mCurX;
    private float mCurY;
    private Paint regionPaint;

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

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

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

    /**
     * 初始化
     */
    private void init(){
        mCurX = -1;
        mCurY = -1;
        startRadius = 0;
        paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(5);
        //初始化圓餅矩形
        rectF = new RectF(20,20, radius * 2, radius * 2);
        rectFTouch = new RectF(0,0, radius * 2 + 20, radius * 2 + 20);

        regionPaint = new Paint();
        regionPaint.setColor(Color.WHITE);
        regionPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        regionPaint.setStrokeWidth(2);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        startRadius = 0;
        //首先判斷是否在圓内
        if (mCurX != -1){
            if ( ((mCurX - radius) * (mCurX - radius) + (mCurY - radius) * (mCurY - radius)) < radius * radius){
                //畫餅圖
                for (int i = 0; i < mCakeBean.size(); i++){
                    paint.setColor(mCakeBean.get(i).getColor());
                    if (isContainPoint(canvas,i)){
                        canvas.drawArc(rectFTouch, startRadius + 5 , mCakeBean.get(i).getRadius() - 10, true, paint);
                    }else{
                        canvas.drawArc(rectF, startRadius, mCakeBean.get(i).getRadius(), true, paint);
                    }
                    startRadius +=  mCakeBean.get(i).getRadius();
                }
                return;
            }
        }
        //畫餅圖
        for (int i = 0; i < mCakeBean.size(); i++){
            paint.setColor(mCakeBean.get(i).getColor());
            canvas.drawArc(rectF, startRadius, mCakeBean.get(i).getRadius(), true, paint);
            startRadius +=  mCakeBean.get(i).getRadius();
        }
    }

    /**
     * 判斷圓弧區域内是否包含某點
     * @param i
     * @return
     */
    private boolean isContainPoint(Canvas canvas,int i){
        Path path = new Path();
        path.moveTo(radius + 10, radius + 10);
        if (i == 0){
            path.lineTo(3 * radius + 10, radius + 10);
        }else{
            path.lineTo((float) (radius + 20 + 2*radius * Math.cos(Math.toRadians(startRadius))),
                    (float) (radius + 20 + 2*radius * Math.sin(Math.sin(Math.toRadians(startRadius)))));
        }
        path.lineTo((float) (radius + 20 + 2*radius * Math.cos(Math.toRadians(startRadius + mCakeBean.get(i).getRadius()))),
                (float) (radius + 20 + 2*radius * Math.sin(Math.sin(Math.toRadians(startRadius + mCakeBean.get(i).getRadius())))));
        path.close();
        Region region = new Region();
        region.setPath(path, new Region(new Rect(20, 20, radius * 2, radius * 2)));
        /*RegionIterator regionIterator = new RegionIterator(region);
        Rect rect = new Rect();
        while (regionIterator.next(rect)){
            canvas.drawRect(rect, paint);
        }*/
        return region.contains((int)mCurX, (int)mCurY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mCurX = event.getX();
        mCurY = event.getY();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                invalidate();
                return true;
            case MotionEvent.ACTION_UP:
                mCurX = -1;
                mCurY = -1;
        }
        invalidate();
        return super.onTouchEvent(event);
    }

    /**
     * 設定資料給餅圖
     * @param cakes
     */
    public void setCakes(ArrayList<CakeBean> cakes){
        this.mCakeBean = cakes;
        invalidate();
    }
}
           

以上就是核心自定義類的代碼,主要難點在于點選換圓弧的半徑,可以重點看一下代碼,實作的邏輯是根據數學公式x平方 + y平方 < 半徑的平方,那麼判斷是在圓上;第二步如果判斷在圓上的話,運用region知識,求交集區域(盡可能把交集區域加大)