圖表之環形圖

最近項目新增一個環形圖表數據查看的。

效果圖如下

在這裏插入圖片描述

在網上找了相關畫環形的,都不符合需求,找到一個類似的代碼RoundRateView,基本畫出了外面圓環,但是有瑕疵,然後再修改

首先根據傳入的4個數值,計算出所佔比例和相應的角度。然後畫弧形 angleList是各個數據對應的角度。當然白色區域也算,所以加起來這個集合的大小就是8。

	float angle=0;
    for (int i = 0; i < angleList.size(); i++) {
        mPaint.setColor(colorList.get(i));
        if (i > 0) {
            startAngle += angleList.get(i - 1);
        }
        canvas.drawArc(oval, startAngle, angleList.get(i), false, mPaint);

    }

效果圖如下

在這裏插入圖片描述

可以看到中間隔層的白色離中心點越遠,越寬。這不難理解,因爲我們畫的是個扇形。
如何讓中間隔層寬度一樣呢。
我這裏是直接在中間隔層畫了個白色的矩形,矩形肯定是寬度一樣的,蓋在上面就可以。這裏還發生一個奇怪的問題,for循環裏面註釋的代碼和後面for循環的代碼一樣,但是在畫弧的for循環裏面,還是沒效果!!!單獨for寫在外面,如下代碼,纔有效果。此問題還不知啥原因。
代碼如下,首先保持圖層,然後在中心點旋轉相應的角度,再畫白色的矩形

 float angle=0;
 for (int i = 0; i < angleList.size(); i++) {
        mPaint.setColor(colorList.get(i));
        if (i > 0) {
            startAngle += angleList.get(i - 1);
        }
        canvas.drawArc(oval, startAngle, angleList.get(i), false, mPaint);
//			 angle+=angleList.get(i);
//            if (angleList.get(i)>1){
//                LogUtil.d("angle:"+angle+","+angleList.get(i));
//                //畫白色分隔帶
//                canvas.save();
//                rectPaint.setColor(Color.WHITE);
//                canvas.rotate(angle, getWidth() / 2, getHeight() / 2);
//                RectF r = new RectF(getWidth() / 2, 0, getWidth() / 2 + ScreenUtil.dip2px(3), mCircleWidth+ScreenUtil.dip2px(4));
//                canvas.drawRect(r, rectPaint);
//                canvas.restore();
//            }
    }

    for (int i=0;i<angleList.size();i++){
		 angle+=angleList.get(i);
        if (angleList.get(i)>1){
            LogUtil.d("angle:"+angle+","+angleList.get(i));
            //畫白色分隔帶
            canvas.save();
            rectPaint.setColor(Color.WHITE);
            canvas.rotate(angle, getWidth() / 2, getHeight() / 2);
            RectF r = new RectF(getWidth() / 2, 0, getWidth() / 2 + ScreenUtil.dip2px(3), mCircleWidth+ScreenUtil.dip2px(4));
            canvas.drawRect(r, rectPaint);
            canvas.restore();
        }
    }

效果如下。白色寬度保持一致了。

在這裏插入圖片描述

剩下來就是畫比例文字了。

在這裏插入圖片描述

只要我們把中心點平移到黑點。則就可以直接drawText畫文字了。
代碼如下。首先我們座標移動到圓中心點。角度a就是弧形角度的一半。外邊圓的半徑,減去弧寬度的一半,就是黑點到圓中心點的距離。然後再乘以角度a的正弦 就是transX.也就是黑點的x座標,同理可得黑點的y座標。再根據字體的寬高調整下位置。就可以畫在黑點的位置。代碼如下

float preAngle=0;
    for (int i=0;i<angleList.size();i++){
        if (i>0) {
            preAngle += angleList.get(i - 1);
        }
        if (angleList.get(i)>1){
            //畫比例數據
            canvas.save();
            //先平移到中心點
            canvas.translate(getWidth()/2,getHeight()/2);
            //將角度轉換爲弧度
            double a=Math.PI/180*(preAngle+angleList.get(i)/2);


            String data=(angleList.get(i)*100/360)+"%";

            float transX = (float) ((getWidth()/2 - mCircleWidth/2)*Math.sin(a))+ViewUtil.getTextRectWidth(textPaint,data)/2;
            float transY = (float) ((getWidth()/2 - mCircleWidth/2)*Math.cos(a))-ViewUtil.getTextHeight(textPaint);

            LogUtil.d("transX:"+transX+",transY:"+transY+","+String.valueOf(data));
            canvas.translate(transX,-transY);
            canvas.drawText(String.valueOf(data),0,0,textRatePaint);
            canvas.restore();
        }
    }

效果圖如下。

在這裏插入圖片描述

完整代碼如下

public class RoundRateView extends View {
    private Paint mPaint;
    private int mCircleWidth; //圓環寬度
    private int intervalAngle = 4;//間隔角度
    private int intervalColor = Color.GRAY;//間隔顏色 默認灰色
    private int aboveTextColor = Color.GRAY;//上面的文字 默認灰色
    private int belowTextColor = Color.GRAY;//下面的文字 默認灰色
    private int aboveTextSize = 60;//上面的文字字體大小
    private int belowTextSize = 40;//下面的文字字體大小
    private boolean isShowText = true; //是否顯示中間文字 默認顯示
    /**
     * 畫文字的畫筆
     */
    private Paint textPaint;
    private Paint textRatePaint;

    private int colors[] = {Color.parseColor("#41A8FF")
            , Color.parseColor("#86C8FF")
            , Color.parseColor("#FF8B13")
            , Color.parseColor("#FFB971")
            , Color.parseColor("#FF8A77")
            , Color.parseColor("#EEE685")
            , Color.parseColor("#EECBAD")
            , Color.parseColor("#EEAEEE")
            , Color.parseColor("#EE3B3B")
            , Color.parseColor("#EDEDED")};


    private List<Integer> angleList = new ArrayList<>(); //所有的角度 集合
    private List<Integer> colorList = new ArrayList<>(); //所有的色值 集合
    private List<Integer> dataList=new ArrayList<>();
    private RectF oval;
    private double allMoney;
    private boolean isShowMoney = true; //是否是明文顯示錢數,默認是明文顯示
    //    private Paint p;

    public RoundRateView(Context context) {
        super(context);
        init(context, null, 0);
    }

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

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

private void init(Context context, AttributeSet attrs, int defStyleAttr) {

    TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundRateView, defStyleAttr, 0);
    int n = array.getIndexCount();//自定義屬性個數
    for (int i = 0; i < n; i++) {
        int attr = array.getIndex(i);//通過索引獲取具體屬性
        switch (attr) {
            case R.styleable.RoundRateView_rrv_circleWidth:
                mCircleWidth = array.getDimensionPixelSize(attr, (int) dip2px(20f));
                if (mCircleWidth < 2) {
                    mCircleWidth = 2;//最小寬度是2
                }
                break;
            case R.styleable.RoundRateView_rrv_intervalAngle:
                intervalAngle = array.getDimensionPixelSize(attr, (int) dip2px(4f));
                break;
            case R.styleable.RoundRateView_rrv_aboveTextSize:
                aboveTextSize = array.getDimensionPixelSize(attr, (int) dip2px(60));
                break;
            case R.styleable.RoundRateView_rrv_belowTextSize:
                belowTextSize = array.getDimensionPixelSize(attr, (int) dip2px(40));
                break;
            case R.styleable.RoundRateView_rrv_intervalColor:
                intervalColor = array.getColor(attr, Color.GRAY);
                break;
            case R.styleable.RoundRateView_rrv_aboveTextColor:
                aboveTextColor = array.getColor(attr, Color.GRAY);
                break;
            case R.styleable.RoundRateView_rrv_belowTextColor:
                belowTextColor = array.getColor(attr, Color.GRAY);
                break;
            case R.styleable.RoundRateView_rrv_isShowText:
                isShowText = array.getBoolean(attr, true);
                break;
        }
    }
    array.recycle();//定義完後屬性對象回


    mPaint = new Paint();  //創建畫筆
    mPaint.setAntiAlias(true);  //設置繪製時抗鋸齒
    mPaint.setStyle(Paint.Style.STROKE); //設置繪畫空心(比如畫圓時畫的是空心圓而不是實心圓)
    mPaint.setStrokeWidth(mCircleWidth);//設置畫筆線寬

    textPaint = new Paint();
    textPaint.setAntiAlias(true);
    textPaint.setDither(true);
    textRatePaint = new Paint();
    textRatePaint.setAntiAlias(true);
    textRatePaint.setDither(true);
    textRatePaint.setTextSize(getResources().getDimension(R.dimen.s20));
    textRatePaint.setColor(Color.WHITE);
    textRatePaint.setTextAlign(Paint.Align.CENTER);
    rectPaint.setAntiAlias(true);
    rectPaint.setStyle(Paint.Style.FILL);
    rectPaint.setColor(Color.WHITE);
    rectPaint.setStrokeWidth(intervalAngle);
}
private Paint rectPaint = new Paint();

public void setList(List<Integer> list) {
    int allIntervalAngle = 0;//所有間隔加起來的角度
    int allModuleAngle;  //所有模塊加起來的角度  allModuleAngle + allIntervalAngle=360;
    if (list.size() > colors.length) {
        return;
    }
    angleList.clear();
    colorList.clear();
    allMoney = 0d;
    for (int i = 0; i < list.size(); i++) {
        allMoney += (list.get(i));
    }
    if (list.size() == 1) { //如果只有一條數據,就不要間隔
        angleList.add(360);
        colorList.add(colors[0]);
    } else {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) != 0) {
                allIntervalAngle += intervalAngle;
            }
        }
        if (allIntervalAngle == intervalAngle) {//如果只有一條數據不爲0,就不要間隔顏色
            angleList.add(360);
            colorList.add(colors[0]);
        } else {
            allModuleAngle = 360 - allIntervalAngle;
            int angle = 0;
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i) != 0) {
                    int e = (int) (list.get(i) / allMoney * allModuleAngle);
                    dataList.add((int) (list.get(i) / allMoney)*100);
                    if (i == list.size() - 1) {//如果是最後一個色塊,所佔角度就是剩餘全部的角度
                        this.angleList.add(allModuleAngle - angle);
                    } else {
                        angle += e;
                        this.angleList.add(e);
                    }
                    this.angleList.add(intervalAngle);
                    this.colorList.add(colors[i]);
                    this.colorList.add(intervalColor);
                }

            }
        }

    }

    invalidate();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int widthPixels = this.getResources().getDisplayMetrics().widthPixels;//獲取屏幕寬
    int heightPixels = this.getResources().getDisplayMetrics().heightPixels;//獲取屏幕高
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int hedight = MeasureSpec.getSize(heightMeasureSpec);
    int minWidth = Math.min(widthPixels, width);
    int minHedight = Math.min(heightPixels, hedight);
    setMeasuredDimension(Math.min(minWidth, minHedight), Math.min(minWidth, minHedight));

}

public void setIsShowText(boolean isShowText) {
    this.isShowText = isShowText;
    invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    drawCircle(canvas);
    if (isShowText) {
        drawText(canvas);
    }
    drawInnerCicle(canvas);
}

private void drawInnerCicle(Canvas canvas) {

    mPaint.setStrokeWidth(ScreenUtil.dip2px(4));
    mPaint.setColor(getResources().getColor(R.color.time_axis_detail_sleep_title));
    int radiu = (int) (getMeasuredWidth()/2 - mCircleWidth - dip2px(4));

    canvas.drawCircle(getMeasuredWidth()/2,getHeight()/2,radiu,mPaint);
}

/**
 * 畫出圓弧
 *
 * @param canvas
 */
private void drawCircle(Canvas canvas) {
    if (oval == null) {
        int min = Math.min(getWidth() - mCircleWidth / 2, getHeight() - mCircleWidth / 2);
        oval = new RectF(mCircleWidth / 2, mCircleWidth / 2,
                min, min);
    }
    float startAngle = -90f;

 
    mPaint.setColor(intervalColor);

    mPaint.setStrokeWidth(mCircleWidth);
    float angle=0;
    for (int i = 0; i < angleList.size(); i++) {
        mPaint.setColor(colorList.get(i));
        if (i > 0) {
            startAngle += angleList.get(i - 1);
        }
        canvas.drawArc(oval, startAngle, angleList.get(i), false, mPaint);
//            angle+=angleList.get(i);
//
//            if (angleList.get(i)>1){
//                LogUtil.d("angle:"+angle+","+angleList.get(i));
//                //畫白色分隔帶
//                canvas.save();
//                rectPaint.setColor(Color.WHITE);
//                canvas.rotate(angle, getWidth() / 2, getHeight() / 2);
//                RectF r = new RectF(getWidth() / 2, 0, getWidth() / 2 + ScreenUtil.dip2px(3), mCircleWidth+ScreenUtil.dip2px(4));
//                canvas.drawRect(r, rectPaint);
//                canvas.restore();
//            }
    }

    for (int i=0;i<angleList.size();i++){
        angle+=angleList.get(i);
        if (angleList.get(i)>1){
            LogUtil.d("angle:"+angle+","+angleList.get(i));
            //畫白色分隔帶
            canvas.save();
            rectPaint.setColor(Color.WHITE);
            canvas.rotate(angle, getWidth() / 2, getHeight() / 2);
            RectF r = new RectF(getWidth() / 2, 0, getWidth() / 2 + ScreenUtil.dip2px(3), mCircleWidth+ScreenUtil.dip2px(4));
            canvas.drawRect(r, rectPaint);
            canvas.restore();
        }
    }
    float preAngle=0;
    for (int i=0;i<angleList.size();i++){
        if (i>0) {
            preAngle += angleList.get(i - 1);
        }
        if (angleList.get(i)>1){
            //畫比例數據
            canvas.save();
            //先平移到中心點
            canvas.translate(getWidth()/2,getHeight()/2);
            //將角度轉換爲弧度
            double a=Math.PI/180*(preAngle+angleList.get(i)/2);


            String data=(angleList.get(i)*100/360)+"%";

            float transX = (float) ((getWidth()/2 - mCircleWidth/2)*Math.sin(a))+ViewUtil.getTextRectWidth(textPaint,data)/2;
            float transY = (float) ((getWidth()/2 - mCircleWidth/2)*Math.cos(a))-ViewUtil.getTextHeight(textPaint);

            LogUtil.d("transX:"+transX+",transY:"+transY+","+String.valueOf(data));
            canvas.translate(transX,-transY);
            canvas.drawText(String.valueOf(data),0,0,textRatePaint);
            canvas.restore();
        }
    }

}

public String precentStr;
public String precentUnit;
/**
 * 畫出中間的文字
 *
 * @param canvas 畫布對象
 */
private void drawText(Canvas canvas) {
    int center = getWidth() / 2;
    String percent;
    percent=precentStr;
    textPaint.setTextSize(aboveTextSize);
    //防止文字邊界超過內環邊界  上面的文字大小減小 下面的文字大小也跟着減小
    while (textPaint.measureText(percent) > getWidth() - 2f * mCircleWidth) {
        aboveTextSize--;
        belowTextSize--;
        textPaint.setTextSize(aboveTextSize);
    }

    textPaint.setTextAlign(Paint.Align.CENTER); // 設置文字居中,文字的x座標要注意
    textPaint.setColor(aboveTextColor); // 設置文字顏色
    textPaint.setStrokeWidth(0); // 注意此處一定要重新設置寬度爲0,否則繪製的文字會重疊
    Rect bounds = new Rect(); // 文字邊框
    textPaint.getTextBounds(percent, 0, percent.length(), bounds); // 獲得繪製文字的邊界矩形
    Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt(); // 獲取繪製Text時的四條線
    int baseline = center + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; // 計算文字的基線,方法見http://blog.csdn.net/harvic880925/article/details/50423762
    canvas.drawText(percent, center, baseline, textPaint); // 繪製文字
    percent=precentUnit;
    textPaint.setColor(belowTextColor); // 設置文字顏色
    textPaint.setTextSize(belowTextSize);
    //防止下面的文字超出內環邊界
    while (textPaint.measureText(percent) > getWidth() - 2f * mCircleWidth) {
        belowTextSize--;
        textPaint.setTextSize(belowTextSize);
    }
    textPaint.getTextBounds(percent, 0, percent.length(), bounds); // 獲得繪製文字的邊界矩形
    Paint.FontMetricsInt fontMetrics1 = textPaint.getFontMetricsInt(); // 獲取繪製Text時的四條線
    int baseline1 = center + (fontMetrics1.bottom - fontMetrics1.top) / 2 - fontMetrics.bottom + 38 * 2;
    canvas.drawText(percent, center, baseline1, textPaint); // 繪製文字
}


public static int px2dip(int pxValue) {
    final float scale = Resources.getSystem().getDisplayMetrics().density;
    return (int) (pxValue / scale + 0.5f);
}


public static float dip2px(float dipValue) {
    final float scale = Resources.getSystem().getDisplayMetrics().density;
    return (dipValue * scale + 0.5f);
}

public void setIsShowMoney(boolean isShowMoney) {
    this.isShowMoney = isShowMoney;
    invalidate();
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章