六边形战士—雷达图实现

    相信大家都看了之前的新闻,世乒赛日本直播版,中二爆表,马龙的六边形战力图全满。

  图是这样的。

      《六边形战士—雷达图实现》

    于是乎想实现一个自定义view实现类似的效果。 这种图正式名称叫雷达图(Radar Chart),又可称为戴布拉图、蜘蛛网图(Spider Chart),是财务分析  报表的一种。但是现在已经应用到很多领域,特别是竞技体育方面对队伍或者选手的实力分析。

  整理了一下思路和查询了一下相关知识,结合前人的代码,实现了自定义雷达图。

  下面写一下实现思路:首先我把雷达图分为底层蜘蛛网+内容区,底层蜘蛛网的六个属性和内容区的六个点分别从2个数组去获取数值,接来下只要依次绘制两层图即可。

    1.初始化

 private int count=6;  //六边形,数据个数6
    private float angle= (float) (Math.PI/3);  //60度
    private double[] data={50,50,50,50,50,50,50}; //默认数据
    private float maxValue=100;     //默认最大值
    private String[] titles={"a","b","c","d","e","f"};  //默认标题

    private Paint radarPaint;                //蜘蛛网画笔
    private  Paint valuePaint;               //内容区画笔
    private Paint textPaint;                 //文字画笔

    private float radius;                   //网格最大半径
    private int centerX;                  //中心X
    private int centerY;                  //中心Y


    public MyRadar(Context context) {
        this(context,null);
    }
    public MyRadar(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MyRadar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    private void init() {

    radarPaint=newPaint();

    radarPaint.setAntiAlias(true);

    radarPaint.setColor(Color.GRAY);

    radarPaint.setStyle(Paint.Style.STROKE);

    valuePaint=newPaint();

    valuePaint.setAntiAlias(true);

    valuePaint.setColor(Color.BLUE);

    valuePaint.setStyle(Paint.Style.FILL_AND_STROKE);

    textPaint=newPaint();

    textPaint.setTextSize(sp2px(15));

    textPaint.setAntiAlias(true);

    textPaint.setStyle(Paint.Style.FILL);

    textPaint.setColor(Color.BLACK);

    }
    @Override

    protected voidonSizeChanged(intw,inth,intoldw,intoldh) {

    radius= Math.min(h, w)/2*0.65f;      //得到半径

    centerX= w/2;                          //得到中心点

    centerY= h/2;

    postInvalidate();

    super.onSizeChanged(w, h, oldw, oldh);

    }

  2,重点的绘图过程来了,第一步,绘制蜘蛛网图  ,绘图之前我们先复习下数学的知识。

  首先,一个正六边形是圆的内接正六边形,每个边对应的圆心角是六十度。

  其次,Android中View的坐标系是我们数学课是不一样的!(很容易被忽视)

  这里的1,2,3,4代表的是象限,因为y的方向不同,导致了象限与数学书中的不同

  《六边形战士—雷达图实现》  

  首先,利用三角函数的知识绘制蜘蛛网图

  cosX对应映射在X轴上长度,sinX对应映射在Y轴上长度。所以可以通过每次X加上60度(1/3PI)去得到边角点。

 private void drawHexagon(Canvas canvas) {
        Path path=new Path();
        float r=radius/(count-1);
        for (int i = 0; i 
     

   效果:

      《六边形战士—雷达图实现》

  接来下绘制标题,我们想要的效果是这样的  标题离边角有一定距离,且呈现对称效果。

  《六边形战士—雷达图实现》

   这时的解决方案是将以比半径稍大的长度作为新的半径,这样可以在六个角外面得到相应的六个点,再在这六个点处绘制标题。

   这里能否直接以六个点为坐标依次绘制文字? 答案是否定的。原因如下图:

     《六边形战士—雷达图实现》

    没错,绘制文字时是将坐标作为文字的左下角,如果不在不同的象限做出处理,文字将无法实现对称。如下:

    《六边形战士—雷达图实现》

    代码:

 private void drawText(Canvas canvas) {

        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent;
        for (int i = 0; i 
     
      =0&&angle*i<=math.pi 2){="" canvas.drawtext(titles[i],="" x,y+fontheight="" 2,textpaint);="" }="" else="" if(angle*i="">Math.PI/2&&angle*i<=math.pi){ float="" dis="textPaint.measureText(titles[i]);" canvas.drawtext(titles[i],="" x-dis,y+fontheight="" 2,textpaint);="" }="" else="" if(angle*i="">=Math.PI&&angle*i<3*math.pi 2){="" float="" dis="textPaint.measureText(titles[i]);" canvas.drawtext(titles[i],="" x-dis,y,textpaint);="" }else="" if(angle*i="">=3*Math.PI/2&&angle*i<=math.pi*2){ canvas.drawtext(titles[i],="" x,y,textpaint);="" }="" }<="" code="">
      
      
      
      
     

    最后绘制内容区域也不难:

private void drawRegion(Canvas canvas) {
        Path path = new Path();
        valuePaint.setAlpha(255);
        for(int i=0;i
     

    完整的draw方法

 @Override
    protected void onDraw(Canvas canvas) {
        drawHexagon(canvas);
        drawText(canvas);
        drawRegion(canvas);
    }

   到这里差不多就结束了,后续就是对外暴露一些方法,以及wrapcontent设置默认大小

 //设置数值
    public void setData(double[] data) {
        this.data = data;
    }


    public float getMaxValue() {
        return maxValue;
    }

    //设置最大数值
    public void setMaxValue(float maxValue) {
        this.maxValue = maxValue;
    }
    //设置标题颜色
    public void setTextPaintColor(int color){
        textPaint.setColor(color);
    }

    //设置覆盖局域颜色
    public void setValuePaintColor(int color){
        valuePaint.setColor(color);

    }
    //设置雷达图颜色
    public void setMainPaintColor(int color){
        radarPaint.setColor(color);
    }
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){
            setMeasuredDimension(sp2px(300),sp2px(300));
        }
        else if (widthMeasureSpec==MeasureSpec.AT_MOST){
            setMeasuredDimension(sp2px(250),heightSpecSize);
        }else if (heightSpecMode==MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSpecSize,sp2px(250));
        }
    }

    最终在acitivty设置数据和标题,最终效果:

public class MainActivity extends AppCompatActivity {
    private MyRadar mRadar;
    double[] data={100,100,100,100,50,100,20};
    String[] titles={"发球","经验","防守","技巧","速度","力量"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRadar= (MyRadar) findViewById(R.id.radar);

        mRadar.setData(data);
        mRadar.setTitles(titles);

    }
}

        《六边形战士—雷达图实现》

    原文作者:Android
    原文地址: https://juejin.im/entry/57ebb0382e958a00545d8bb0
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞