Android自定义View实现饼图

最终效果

在这里插入图片描述

代码实现
package com.soface.chartdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

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

public class PieChartView extends View {

    public PieChartView(Context context) {
        this(context,null);
    }

    public PieChartView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public PieChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        colors=new ArrayList<>();
        colors.add("#FF5722");
        colors.add("#FF9800");
        colors.add("#00BCD4");
        colors.add("#03A9F4");
        colors.add("#FFC107");
        colors.add("#2196F3");
        colors.add("#CDDC39");
        colors.add("#3F51B5");
        colors.add("#8BC34A");
        colors.add("#673AB7");
        colors.add("#9C27B0");
        colors.add("#8BC34A");
        colors.add("#F44336");
        colors.add("#009688");
        colors.add("#B1B1B1");

    }

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

        CENTER_X=widthSize/2f;
        CENTER_Y=heightSize/2f;
        if (widthSize>=heightSize){
            RADIUS=heightSize/2f-80;
        }else {
            RADIUS=widthSize/2f-80;
        }

        initPaint();
    }

    private Paint piePaint;

    private Paint linePaint;

    private Paint textPaint;

    private Paint titlePaint;

    private RectF mRectF; //饼图矩阵

    private float CENTER_X = 0; //中心点横坐标
    private float CENTER_Y = 0; //中心点纵坐标
    private float RADIUS = 0; //圆半径
    private List<String> colors;//饼图的颜色列表
    private List<String> items;//饼图功能项
    private List<Integer> dataList;//数据列表
    private String unit="";//数据单位
    private float step=0;//进率步距,360的角度怎么分配给数据
    private String titleStr="";

    private void initPaint() {
        piePaint =new Paint();
        piePaint.setStrokeWidth(4);
        piePaint.setAntiAlias(true);//抗锯齿功能

        linePaint =new Paint();
        linePaint.setStrokeWidth(2);
        linePaint.setColor(Color.parseColor("#858585"));
        linePaint.setAntiAlias(true);//抗锯齿功能
        /*PathEffect effects = new DashPathEffect(new float[]{5, 10}, 0);//设置绘制虚线
        linePaint.setPathEffect(effects);*/

        textPaint =new Paint();
        textPaint.setColor(Color.parseColor("#858585"));
        textPaint.setAntiAlias(true);//抗锯齿功能
        textPaint.setTextSize(30);

        titlePaint =new Paint();
        titlePaint.setColor(Color.parseColor("#858585"));
        titlePaint.setAntiAlias(true);//抗锯齿功能
        titlePaint.setTextSize(30);

    }


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

        if (items==null || items.size()==0)return;
        if (dataList==null || dataList.size()==0)return;
        if (colors==null || colors.size()==0)return;
        if (items.size() != dataList.size())return;//要求 功能项长度 等于 数据项长度

        float dataSum=0;
        for (int k=0;k<dataList.size();k++){
            dataSum=dataList.get(k)+dataSum;
        }
        step=360/dataSum;

        float startAngle=0;//开始绘制的角度
        for (int k=0;k<dataList.size();k++){

            if (k<colors.size()){
                //按照顺序取颜色
                piePaint.setColor(Color.parseColor(colors.get(k)));
            }else {
                //超出颜色列表的数据 取最后一种颜色
                piePaint.setColor(Color.parseColor(colors.get(colors.size()-1)));
            }

            float thisAngle=dataList.get(k)*step;//本次次循环需要绘制的角度

            if (k==dataList.size()-1){
                //最后一块饼图跳出
                //绘制扇形
                canvas.drawArc(CENTER_X - RADIUS+14, CENTER_Y - RADIUS-10, CENTER_X + RADIUS+14, CENTER_Y + RADIUS-10, startAngle, thisAngle, true, piePaint);
            }else {
                //绘制扇形
                canvas.drawArc(CENTER_X - RADIUS, CENTER_Y - RADIUS, CENTER_X + RADIUS, CENTER_Y + RADIUS, startAngle, thisAngle, true, piePaint);
            }

            float lineAngle = startAngle + thisAngle/2f; //获取线条的角度

            startAngle=thisAngle+startAngle;//叠加起步角度

            //分别求线条的横坐标起点、纵坐标起点、横坐标终点、纵坐标终点
            float lineStartX = CENTER_X + RADIUS * (float) Math.cos(lineAngle / 180 * Math.PI);
            float lineStartY = CENTER_Y + RADIUS * (float) Math.sin(lineAngle / 180 * Math.PI);
            float lineEndX = CENTER_X + (RADIUS + 20) * (float) Math.cos(lineAngle / 180 * Math.PI);
            float lineEndY = CENTER_Y + (RADIUS + 20) * (float) Math.sin(lineAngle / 180 * Math.PI);

            if (k==dataList.size()-1){
                //最后一条线跳出
                lineStartX += 14;
                lineStartY -= 10;
                lineEndX += 14;
                lineEndY -= 10;
            }
            canvas.drawLine(lineStartX, lineStartY, lineEndX, lineEndY, linePaint); //绘制指示线条

            if (lineEndX>CENTER_X){
                //在圆心右侧   向右延长
                canvas.drawLine(lineEndX, lineEndY, lineEndX+200, lineEndY, linePaint); //绘制延长线
                canvas.drawText(items.get(k)+": "+dataList.get(k)+"("+unit+")",lineEndX+200-100,lineEndY-10,textPaint);//绘制数据值
            }else {
                //在圆心左侧   向左延长
                canvas.drawLine(lineEndX, lineEndY, lineEndX-200, lineEndY, linePaint); //绘制延长线
                canvas.drawText(items.get(k)+": "+dataList.get(k)+"("+unit+")",lineEndX-200,lineEndY-10,textPaint);//绘制数据值
            }


        }

        //画中圆
        piePaint.setColor(Color.parseColor("#FFFFFF"));
        canvas.drawCircle(CENTER_X,CENTER_Y,RADIUS/2, piePaint);

        canvas.drawText(titleStr,CENTER_X-RADIUS/2+20,CENTER_Y+10,textPaint);//绘制数据值
    }

    /**
     * 设置数据
     * @param dataList
     */
    public synchronized void setData(List<Integer> dataList,String unit){
        this.dataList=dataList;
        this.unit=unit;
        postInvalidate();
    }

    /**
     * 设置对比模块项
     * @param items
     */
    public void setItemList(List<String> items){
        this.items=items;
        postInvalidate();
    }

    /**
     * 设置标题
     * @param titleStr
     */
    public void setTitle(String titleStr){
        this.titleStr=titleStr;
        postInvalidate();
    }

    /**
     * 设置标题大小
     * @param sizePx
     */
    public void setTitleSize(float sizePx){
        titlePaint.setTextSize(sizePx);
    }

}
使用方法

1.在XML中引用

<com.example.chartdemo.PieChartView
                android:id="@+id/pie_chart"
                android:background="#EFEFEF"
                android:layout_margin="20px"
                android:layout_width="match_parent"
                android:layout_height="600px" />
  1. 设置参数数据
List<Integer> pieChatDatas=new ArrayList<>();
        pieChatDatas.add(50);
        pieChatDatas.add(60);
        pieChatDatas.add(30);
        pieChatDatas.add(30);
        pieChatDatas.add(50);
        pieChatDatas.add(60);
        pieChatDatas.add(70);

        List<String> pieChatItems=new ArrayList<>();
        pieChatItems.add("大狗");
        pieChatItems.add("二狗");
        pieChatItems.add("张三");
        pieChatItems.add("李四");
        pieChatItems.add("王武");
        pieChatItems.add("老刘");
        pieChatItems.add("阿七");

        PieChartView pie_Chart=(PieChartView) findViewById(R.id.pie_chart);
        pie_Chart.setItemList(pieChatItems);
        pie_Chart.setData(pieChatDatas,"个");
        pie_Chart.setTitle("家中梨多少?");

		//==================下面是编辑数据实现动效==========================
		int forTag=-10;//该变量放全局
		int jiShu=pieChatDatas.get(0);
        int jiShuIndex=0;
        //循环请求
        Heartbeat.getInstance().start(50,new ActionCallback() {
            @Override
            public void toDo(Object object) {
                if (forTag<=30){

                    //模拟二次函数 对称轴是x = -b/(2a) =50
                    int bianLiang=-1*(forTag*forTag)+30*forTag;

                    pieChatDatas.set(jiShuIndex,jiShu+bianLiang);

                    pie_Chart.setData(pieChatDatas,"个");

                    Heartbeat.getInstance().reset(50);
                }
                forTag=forTag+1;
            }
        });

附上两个附属类(与图表实现无关)

package com.soface.chartdemo;

import android.os.Looper;
import android.os.Message;

/**
 * 心跳计时器
 */
public class Heartbeat {

    //事件标签
    public final int ACTION_TAG =0xFF;
    //要回调的接口
    private ActionCallback thisActionDo;

    //单例的基本操作
    private static Heartbeat myCountDownTimer;
    private Heartbeat() { }
    public static Heartbeat getInstance(){
        if (myCountDownTimer==null)myCountDownTimer=new Heartbeat();
        return myCountDownTimer;
    }

    //Handler事件监听器
    private android.os.Handler mHandler = new android.os.Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case ACTION_TAG:
                    thisActionDo.toDo(null);
                    break;
            }

        }
    };

    //首次启动心跳(倒计时)
    public void start(int countDownTime,ActionCallback actionDo){
        thisActionDo=actionDo;
        reset(countDownTime);
    };

    //重置倒计时
    public void reset(int countDownTime){
        mHandler.removeMessages(ACTION_TAG);
        Message msg = mHandler.obtainMessage();
        msg.what = ACTION_TAG;
        mHandler.sendEmptyMessageDelayed(ACTION_TAG,countDownTime);
    }

}
package com.soface.chartdemo;

public interface ActionCallback {

    void toDo(Object o);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝命三郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值