Android圆形饼图绘制(当饼图占比很小时描述文字的分开绘制)

综合技术 简书

由于项目中需要用到饼图,用MpAndroidChart,当饼图部分占比很小时,描述文字重叠,所以自己重新绘制了饼图,并提供饼图各部分的点击监听,效果图如下:

转成动态图时效果不好,实际颜色是正常的

绘制饼图的类:

package com.karoline.views.bars;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.karoline.utils.SizeUtils;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by ${Karoline} on 2017/6/14.
 */

public class AssetKcPie extends View {//继承View类
    private Context mContext;
    private Paint textPaint; 
    private Paint arcPaint;
    private Paint linePaint;

    private WeakReference bitmapBuffer;
    private Canvas bitmapCanvas;

    private float distance;
    private float radius;
    private int barWidth,barHeight;
    private List datas;
    private List angleSEs;
    private List lengedRectes;
    private OnSelectedListener mListener;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            float actionX = event.getX(); //点击点的坐标
            float actionY = event.getY();
            double distance = Math.sqrt(Math.pow(Math.abs(actionX-barWidth/2),2)+
                                Math.pow(Math.abs(actionY-barHeight/2),2));
            double angle = Math.atan((actionY-barHeight/2) /(actionX-barWidth/2)) /3.14 * 180 - 90;
            float X = barWidth/2,Y=(barHeight-lengedHeight)/2;
            if(actionX > X && actionY X && actionY>Y) {
                angle = 90+angle;
            }else if (actionX Y) {
                angle = 270-angle;
            }else if (actionX < X && actionY<Y) {
                angle = 270+angle;
            }

            if(angleSEs == null || angleSEs.size() == 0 || mListener == null) return false;

            for(int i=0;i<angleSEs.size();i++){
                if(distance  angleSEs.get(i).getStartAngle() && angle<angleSEs.get(i).getSweepAngle()){
                        mListener.onSelected(i); //当点击点在圆内且在扇形上时,触发监听事件
                    }
                }else if(lengedRectes.get(i).contains(actionX,actionY)){
                    mListener.onSelected(i); //当点击点在描述文字上时,触发监听事件(此处是当饼图部分太小,无法点击时的补充)
                }
            }
            return false;
        }

        return super.onTouchEvent(event);
    }

    private float totalNum;

    private int lengedHeight = 0;
    private boolean isLengedVisible = false;

    public AssetKcPie(Context context) {
        super(context);
    }

    public AssetKcPie(Context context, AttributeSet attrs) {
        super(context, attrs); 

        mContext = context;

        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setColor(Color.BLACK);
        textPaint.setTextSize(SizeUtils.dp2px(context,11));

        arcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        arcPaint.setTextSize(radius);

        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setColor(Color.DKGRAY);
        linePaint .setTextSize(3);

        distance = SizeUtils.dp2px(context,16);

        setRadius(SizeUtils.dp2px(context,80));

    }

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

    public void setRadius(float rs){
        this.radius = rs;
    }

    public void setData(List dataS,float total){
        this.datas = dataS;
        this.totalNum = total;
        invalidate();
    }

    public void setLenged(){ //设置是否绘制控件下方的描述
        isLengedVisible = true;
        lengedHeight = (int) SizeUtils.dp2px(mContext,32);
                setMeasuredDimension(onWidthMeasure(getMeasuredWidth()),onHeightMeasure(getMeasuredHeight()));
    }

    private void drawLenged(){ //绘制控件下面的描述
        lengedRectes = new ArrayList();
        Rect rect = new Rect();
        float lengedX = distance;
        float lengedY = barHeight - lengedHeight + distance;
        float totalWidth;
        for(int i = 0;i barWidth){//判断描述的长度是否大于一行
                    lengedY = lengedY +distance;
                    lengedX = distance;
                }

                bitmapCanvas.drawRect(lengedX,lengedY-distance/2,lengedX+distance/2,lengedY ,arcPaint);//绘制描述的颜色方块
                rectF.left = lengedX;
                rectF.top = lengedY-distance;
                lengedX = lengedX + distance/2 + 4;
                bitmapCanvas.drawText(datas.get(i).getDesc(),lengedX,lengedY,textPaint);//绘制描述的颜色文字
                lengedX = lengedX + rect.width() + distance;
                rectF.right = lengedX;
                rectF.bottom = lengedY + distance;
                lengedRectes.add(rectF);//将描述说在的Recf存起来备用(注意放大点击的热区)
            }
        }

        bitmapCanvas.save();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int Width = onWidthMeasure(widthMeasureSpec); //计算控件的宽度
        int height = onHeightMeasure(heightMeasureSpec);//计算控件的高度
        setMeasuredDimension(Width,height);
    }

    //当控件的宽度,高度发生变化是调用
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh); 
        int width = getMeasuredWidth();
        int hight = getMeasuredHeight();
        if (bitmapBuffer == null
                || (bitmapBuffer.get().getWidth() != width)
                || (bitmapBuffer.get().getHeight() != hight)) {

            if (width > 0 && hight > 0) {

                bitmapBuffer = new WeakReference(Bitmap.createBitmap(width, hight, Bitmap.Config.ARGB_4444));
                bitmapCanvas = new Canvas(bitmapBuffer.get());
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.save();
        bitmapBuffer.get().eraseColor(Color.TRANSPARENT);//绘制时先擦除画板上的内容(用于更新界面)

        if(datas != null && datas.size() > 0 ){
            if(isLengedVisible){
                drawLenged();
            }

            angleSEs = new ArrayList();

            RectF arcRect = new RectF(barWidth/2-radius,(barHeight-lengedHeight)/2 - radius,
                    barWidth/2+radius,(barHeight-lengedHeight)/2 + radius);//圆形所在的RectF,圆心与控件中心重叠
            float startAngle = -90,sweepAngle = 0;
            float perAngle = totalNum/360;//每一度的数量
            String desc;
            float lineAngle;

            Rect rect = new Rect();
            for(int i=0;i<datas.size();i++){
                sweepAngle = datas.get(i).getNum()/perAngle; //当前值的度数
                arcPaint.setColor(datas.get(i).getColor());
                bitmapCanvas.drawArc(arcRect,startAngle,sweepAngle,true,arcPaint);//绘制扇形
                angleSEs.add(new AngleSE(startAngle,sweepAngle+startAngle));

                lineAngle = startAngle+ sweepAngle/2;//绘制描述文字的指示线,从扇形中间开始
                desc = datas.get(i).getDesc()+","+datas.get(i).getSNum();
                drwaLineAndText(sweepAngle,lineAngle,desc,rect,i);

                startAngle += sweepAngle; *//开始角度变为扇形结束的角度,下次绘制时从前一个扇形的结束区绘制*
            }
            bitmapCanvas.save();
            bitmapCanvas.restore();
        }

        Rect displayRect = new Rect(0,0,barWidth,barHeight);
        Rect det = new Rect(0,0,getWidth(),getHeight());

        canvas.drawBitmap(bitmapBuffer.get(),displayRect,det,null);
        canvas.restore();
    }

    private void drwaLineAndText(float sweepAngle,float lineAngle,String desc,Rect rect,int i){
        float lineStartX,lineStartY ,lineEndX,lineEndY ;

        lineStartX   =   barWidth/2   +   (radius- distance)   *  (float) Math.cos(lineAngle *   3.14   /180 );
        lineStartY   =   (barHeight-lengedHeight)/2   +   (radius- distance)  *   (float) Math.sin(lineAngle   *   3.14/180);
        if(Math.abs(sweepAngle) barWidth/2) { //当指示线位于饼图右侧时,在右侧绘制第二条指示线及文字
            bitmapCanvas.drawLine(lineEndX,lineEndY,lineEndX+distance/2,lineEndY,linePaint);
            bitmapCanvas.drawText(desc,lineEndX+distance/2+4,lineEndY+rect.height()/2,textPaint);
        }else {//当指示线位于饼图左侧时,在左侧绘制第二条指示线及文字

            bitmapCanvas.drawLine(lineEndX,lineEndY,lineEndX-distance/2,lineEndY,linePaint);
            bitmapCanvas.drawText(desc,lineEndX-distance/2-4-rect.width(),lineEndY+rect.height()/2,textPaint);
        }
    }

    private int onWidthMeasure(int width){
        int mode = MeasureSpec.getMode(width);
        int size = MeasureSpec.getSize(width);

        if(mode == MeasureSpec.EXACTLY){
            barWidth = size;
        }else if(mode == MeasureSpec.AT_MOST){
            barWidth = width - getPaddingLeft() - getPaddingRight();
        }
        return barWidth;
    }


    private int onHeightMeasure(int height){
        int mode1 = MeasureSpec.getMode(height);
        int size1 = MeasureSpec.getSize(height);
        int minSize = (int) SizeUtils.dp2px(mContext,120);
        if(mode1 == MeasureSpec.EXACTLY){
            barHeight = size1;
        }else { //当控件的高度为wrapContet时计算控件的高度
            if(datas != null && datas.size()>0){
                barHeight = (int) radius*2 + lengedHeight + (int) distance*2 + (int) distance*datas.size() ;
            }else {
                barHeight = minSize - getPaddingTop() - getPaddingBottom();
            }
        }

        return barHeight;
    }

    public void setOnSelectedListener(OnSelectedListener l){
        mListener = l;
    }

    public class AngleSE{
        private float startAngle;
        private float sweepAngle;

        public AngleSE(float startAngle, float sweepAngle) {
            this.startAngle = startAngle;
            this.sweepAngle = sweepAngle;
        }

        public float getStartAngle() {
            return startAngle;
        }

        public float getSweepAngle() {
            return sweepAngle;
        }
    }

    public interface OnSelectedListener{ //点击监听接口
        void onSelected(int position);
    }
}

调用示例:

package com.karoline.uiviews;

import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import com.karoline.R;
import com.karoline.codelibrary.BaseToolBarActivity;
import com.karoline.views.bars.AssetKcData;
import com.karoline.views.bars.AssetKcPie;
import com.karoline.views.bars.AssetLenged;

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

import butterknife.BindView;

public class AsssetKcActivity extends BaseToolBarActivity implements AssetKcPie.OnSelectedListener {
    @BindView(R.id.assetkc_desc)
    TextView assetkcDesc;
    @BindView(R.id.assetkc_chart)
    AssetKcPie assetkcPie;
    @BindView(R.id.assetkc_desc1)
    TextView assetkcDesc1;
    @BindView(R.id.assetkc_chart1)
    AssetKcPie assetkcChart1;
    @BindView(R.id.assetkc_lenged)
    AssetLenged assetkcLenged;

    private int mode = 1;
    private List perColorList;
    private List descList = new ArrayList();
    private List kcDatas;


    @Override
    protected int getContentView() {
        return R.layout.activity_assset_kc;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        setTitle("库存概览");
        assetkcPie.setOnSelectedListener(this);//添加监听
        assetkcPie.setLenged();

        assetkcDesc1.setText("");

        kcDatas = new ArrayList();
        perColorList = new ArrayList();
        perColorList.add(Color.parseColor("#f36c60"));
        perColorList.add(Color.parseColor("#fba6c8"));
        perColorList.add(Color.parseColor("#7986cb"));
        perColorList.add(Color.parseColor("#4fc3f7"));
        perColorList.add(Color.parseColor("#cfd8dc"));
        perColorList.add(Color.parseColor("#4db6ac"));
        perColorList.add(Color.parseColor("#aed581"));
        perColorList.add(Color.parseColor("#fff176"));
        perColorList.add(Color.parseColor("#ffb74d"));


        updateView();
    }


    private void updateView() {
        List list = new ArrayList();
        list.add(new AssetKcData(Color.rgb(205, 92, 92), "工器具", 318243f));
        list.add(new AssetKcData(Color.rgb(255, 193, 37), "消耗品", 674937f));
        list.add(new AssetKcData(Color.parseColor("#cfd8dc"), "设施设备工装", 212664f));
        list.add(new AssetKcData(Color.rgb(10, 149, 237), "原材料", 35420464f));
        assetkcPie.setData(list, 318243 + 674937 + 212664 + 35420464);
    }

    @Override
    public void onSelected(int position) {
        List list = new ArrayList();
        switch (position) {
            case 0:
                assetkcDesc1.setText("工器具金额");
                assetkcChart1.setVisibility(View.VISIBLE);
                list.clear();
                list.add(new AssetKcData(perColorList.get(0), "电动工具", 38189.16f));
                list.add(new AssetKcData(perColorList.get(1), "手动工具", 25459.44f));
                list.add(new AssetKcData(perColorList.get(2), "仪器仪表", 12729.72f));
                list.add(new AssetKcData(perColorList.get(3), "照明灯具", 12729.72f));
                list.add(new AssetKcData(perColorList.get(4), "通讯工具", 25459.44f));
                list.add(new AssetKcData(perColorList.get(5), "杂项工具", 12729.72f));

                descList.clear();
                descList.add("电动工具");
                descList.add("手动工具");
                descList.add("仪器仪表");
                descList.add("照明灯具");
                descList.add("通讯工具");
                descList.add("杂项工具");

                assetkcChart1.setData(list, 127297.20f);
                assetkcLenged.setData(perColorList, descList);
                break;
            case 1:
                assetkcDesc1.setText("消耗品金额");
                assetkcChart1.setVisibility(View.VISIBLE);
                list.clear();
                list.add(new AssetKcData(perColorList.get(0), "刀具", 134987.4f));
                list.add(new AssetKcData(perColorList.get(1), "防寒防汛", 53994.96f));
                list.add(new AssetKcData(perColorList.get(2), "劳防用品", 80992.44f));

                descList.clear();
                descList.add("刀具");
                descList.add("防寒防汛");
                descList.add("劳防用品");

                assetkcChart1.setData(list, 269974.8f);
                assetkcLenged.setData(perColorList, descList);
                break;
            case 2:
                assetkcDesc1.setText("设施设备工装金额");
                assetkcChart1.setData(null, 100f);
                assetkcLenged.setData(null, null);
                break;
            case 3:
                assetkcDesc1.setText("原材料金额");
                assetkcChart1.setVisibility(View.VISIBLE);
                list.clear();
                list.add(new AssetKcData(perColorList.get(0), "备品备件(专用)", 1416818.56f));
                list.add(new AssetKcData(perColorList.get(1), "备品备件(通用)", 1558500.416f));
                list.add(new AssetKcData(perColorList.get(2), "紧固件", 4250455.68f));
                list.add(new AssetKcData(perColorList.get(3), "水暖配件", 708409.28f));
                list.add(new AssetKcData(perColorList.get(4), "电器电料", 2833637.12f));
                list.add(new AssetKcData(perColorList.get(5), "灯类", 2125227.84f));
                list.add(new AssetKcData(perColorList.get(6), "辅料", 566727.424f));
                list.add(new AssetKcData(perColorList.get(7), "材料", 283363.712f));
                list.add(new AssetKcData(perColorList.get(8), "化工原料", 425045.568f));

                descList.clear();
                descList.add("备品备件(专用)");
                descList.add("备品备件(通用)");
                descList.add("紧固件");
                descList.add("水暖配件");
                descList.add("电器电料");
                descList.add("灯类");
                descList.add("辅料");
                descList.add("材料");
                descList.add("化工原料");

                assetkcChart1.setData(list, 14168185.6f);
                assetkcLenged.setData(perColorList, descList);
                break;
        }
    }
}

附调用示例的xml文件:

    

    

        

        

            
        
    

    

        

        

            
        
        
    
    
简书稿源:简书 (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合技术 » Android圆形饼图绘制(当饼图占比很小时描述文字的分开绘制)

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录