Android自定义View实现雷达统计图

效果展示

在这里插入图片描述

代码实现
package com.soface.chartdemo.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.List;

public class RadarChartView2 extends View {

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

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

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


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

        widthMode = MeasureSpec.getMode(widthMeasureSpec);
        heightMode = MeasureSpec.getMode(heightMeasureSpec);
        widthSize = MeasureSpec.getSize(widthMeasureSpec);
        heightSize = MeasureSpec.getSize(heightMeasureSpec);

        intiConstant();

        initPaint();
    }

    private float RADIUS =0;
    private float cX=0;
    private float cY=0;
    private float LADDER=5;//阶梯数
    private float DATA_MAX=100;//数据最大值
    private void intiConstant() {
        if (widthSize>heightSize){
            RADIUS =heightSize/2f-20;
        }else {
            RADIUS =widthSize/2f-20;
        }
        cX=widthSize/2f;
        cY=heightSize/2f;
    }

    private Paint linePaint1;
    private Paint textPaint;
    private Paint dataPaint;
    private void initPaint() {
        linePaint1 =new Paint();
        linePaint1 .setAntiAlias(true);//消除锯齿
        linePaint1.setColor(Color.parseColor("#8F8F8F"));
        linePaint1.setStyle(Paint.Style.STROKE);//不加这个不显示
        linePaint1.setStrokeWidth(2);
        linePaint1.setAntiAlias(true);//抗锯齿功能
        PathEffect effects = new DashPathEffect(new float[]{5, 10}, 0);//设置绘制虚线
        linePaint1.setPathEffect(effects);

        textPaint =new Paint();
        textPaint .setAntiAlias(true);//消除锯齿
        textPaint.setColor(Color.parseColor("#393939"));
        textPaint.setTextSize(30);
        linePaint1.setStrokeWidth(2);

        dataPaint =new Paint();
        dataPaint .setAntiAlias(true);//消除锯齿
        dataPaint.setColor(Color.parseColor("#03A9F4"));
        dataPaint.setStyle(Paint.Style.STROKE);//不加这个不显示
        dataPaint.setStrokeWidth(4);
        dataPaint.setAntiAlias(true);//抗锯齿功能
        dataPaint.setStyle(Paint.Style.FILL_AND_STROKE); //设置绘图模式 扫描 FILL_AND_STROKE
        dataPaint.setStrokeJoin(Paint.Join.ROUND); //设置线条闭合模式 MITER
    }

    private List<Integer> dataList;
    public void setData(List<Integer> list){
        dataList=list;
        postInvalidate();
    }

    private List<String> functionsList;
    private String unit="";//单位
    public void setFunction(List<String> list,String unit){
        functionsList=list;
        this.unit=unit;
        postInvalidate();
    }

    private float onceAngle=0;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (functionsList==null || functionsList.size()==0)return;
        if (dataList==null || dataList.size()==0)return;
        if (dataList.size() != functionsList.size())return;

        float functionsSize=functionsList.size();
        onceAngle=360/functionsSize;

        for (int k=0;k<functionsList.size();k++){
            //绘制角度分割线
            float lineEndX = cX + RADIUS * (float) Math.cos((k*onceAngle) / 180 * Math.PI);
            float lineEndY = cY + RADIUS * (float) Math.sin((k*onceAngle) / 180 * Math.PI);
            canvas.drawLine(cX,cY,lineEndX,lineEndY,linePaint1);

            //绘制功能项名称
            if (lineEndX>cX){
                //在圆心右侧
                textPaint.setColor(Color.parseColor("#393939"));
                canvas.drawText(functionsList.get(k),lineEndX+40,lineEndY,textPaint);
            }else {
                //在圆心左侧
                textPaint.setColor(Color.parseColor("#393939"));
                canvas.drawText(functionsList.get(k),lineEndX-80,lineEndY,textPaint);
            }

        }

        float onceLadderRadius=RADIUS/LADDER;
        int ladderData= (int) (DATA_MAX/LADDER);
        for (int k=0;k<LADDER;k++){

            //绘制刻度
            float textX = cX + (onceLadderRadius*k) * (float) Math.cos((0) / 180 * Math.PI);
            float textY = cY + (onceLadderRadius*k) * (float) Math.sin((0) / 180 * Math.PI);
            if (k==LADDER-1){
                textPaint.setColor(Color.parseColor("#FF5722"));
                canvas.drawText(ladderData*k+"("+unit+")",textX,textY,textPaint);
            }else {
                textPaint.setColor(Color.parseColor("#FF5722"));
                canvas.drawText(ladderData*k+"",textX,textY,textPaint);
            }
        }

        for (int k=0;k<LADDER;k++){

            if (k==0)linePaint1.setColor(Color.parseColor("#FF5722"));
            if (k==1)linePaint1.setColor(Color.parseColor("#FF9800"));
            if (k==2)linePaint1.setColor(Color.parseColor("#CDDC39"));
            if (k==3)linePaint1.setColor(Color.parseColor("#4CAF50"));
            if (k==4)linePaint1.setColor(Color.parseColor("#03A9F4"));
            if (k>=5)linePaint1.setColor(Color.parseColor("#8F8F8F"));

            for (int a=0;a<functionsList.size();a++){

                float startX=cX + (onceLadderRadius*k) * (float) Math.cos((a*onceAngle) / 180 * Math.PI);
                float startY=cY + (onceLadderRadius*k) * (float) Math.sin((a*onceAngle) / 180 * Math.PI);

                float endX=cX + (onceLadderRadius*k) * (float) Math.cos(((a+1)*onceAngle) / 180 * Math.PI);
                float endY=cY + (onceLadderRadius*k) * (float) Math.sin(((a+1)*onceAngle) / 180 * Math.PI);

                Path path=new Path();
                path.moveTo(startX,startY);
                path.lineTo(endX,endY);
                //画线连接下一个点
                canvas.drawPath(path,linePaint1);

            }

        }

        //======================绘制数据=============================================================
        float dataStep=RADIUS/DATA_MAX;
        for (int k=0;k<dataList.size();k++){

            if (k!=dataList.size()-1){
                float startX = cX + (dataStep*dataList.get(k)) * (float) Math.cos((k*onceAngle) / 180 * Math.PI);
                float startY = cY + (dataStep*dataList.get(k)) * (float) Math.sin((k*onceAngle) / 180 * Math.PI);

                float endX = cX + (dataStep*dataList.get(k+1)) * (float) Math.cos(((k+1)*onceAngle) / 180 * Math.PI);
                float endY = cY + (dataStep*dataList.get(k+1)) * (float) Math.sin(((k+1)*onceAngle) / 180 * Math.PI);

                Path path=new Path();
                path.moveTo(startX,startY);
                path.lineTo(endX,endY);
                //画线连接下一个点
                canvas.drawPath(path,dataPaint);

                //画一个点
                canvas.drawCircle(startX,startY,6,dataPaint);
            }else {
                //最后一次画回原点
                float startX = cX + (dataStep*dataList.get(k)) * (float) Math.cos((k*onceAngle) / 180 * Math.PI);
                float startY = cY + (dataStep*dataList.get(k)) * (float) Math.sin((k*onceAngle) / 180 * Math.PI);

                float endX = cX + (dataStep*dataList.get(0)) * (float) Math.cos((0*onceAngle) / 180 * Math.PI);
                float endY = cY + (dataStep*dataList.get(0)) * (float) Math.sin((0*onceAngle) / 180 * Math.PI);

                Path path=new Path();
                path.moveTo(startX,startY);
                path.lineTo(endX,endY);
                //画线连接下一个点
                canvas.drawPath(path,dataPaint);

                //画一个点
                canvas.drawCircle(startX,startY,6,dataPaint);
            }


        }

    }
}

使用方法
  1. 在xml中引用
<com.soface.chartdemo.view.RadarChartView2
                android:id="@+id/radar2"
                android:layout_margin="20px"
                android:layout_width="match_parent"
                android:layout_height="600px"/>
  1. 注入数据
private List<Integer> radarDatas2;
    private RadarChartView2 radar2;
    private void initRadarChart2() {
        radarDatas2=new ArrayList<>();
        radarDatas2.add(60);
        radarDatas2.add(40);
        radarDatas2.add(85);
        radarDatas2.add(50);
        radarDatas2.add(70);
        radarDatas2.add(30);
        radarDatas2.add(40);

        List<String> radarFunctions=new ArrayList<>();
        radarFunctions.add("齐国");
        radarFunctions.add("楚国");
        radarFunctions.add("秦国");
        radarFunctions.add("燕国");
        radarFunctions.add("赵国");
        radarFunctions.add("魏国");
        radarFunctions.add("韩国");

        radar2=(RadarChartView2) findViewById(R.id.radar2);
        radar2.setData(radarDatas2);
        radar2.setFunction(radarFunctions,"万");

        doRadarChart2();
    }
    private void doRadarChart2() {

        for (int a=0;a<radarDatas2.size();a++){
            new BarHeartbeat().start(30, a, radarDatas2.get(a), new ActionCallback() {
                @Override
                public void toDo(Object o) {
                    List<Integer> list= (List<Integer>) o;
                    radarDatas2.set(list.get(0),list.get(1));
                    radar2.setData(radarDatas2);
                }
            });
        }

    }

辅助类

package com.soface.chartdemo.util;

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

import com.soface.chartdemo.Interface.ActionCallback;

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

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

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

    //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:
                    List<Integer> list= (List<Integer>) msg.obj;
                    thisActionDo.toDo(list);
                    break;
            }

        }
    };

    //启动
    public void start(int countDownTime,int index,int maxNumber,ActionCallback actionDo){

        thisActionDo=actionDo;

        for (int a=0;a<=maxNumber;a++){
            Message msg = mHandler.obtainMessage();
            msg.what = ACTION_TAG;

            List<Integer> list=new ArrayList<>();
            list.add(index);
            list.add(a);

            msg.obj=list;

            mHandler.sendMessageDelayed(msg,countDownTime*a);
        }
    };

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

绝命三郎

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

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

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

打赏作者

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

抵扣说明:

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

余额充值