Android画正N边形战力图

总体实现的目标如下:

我们要实现一个战斗力的网状图,可以随意改变网状图的边数,从外面传入边数后可以自动调节各个属性值,图示为正4、7、8、13边形的战力图表。

根据图中显示,整体实现步骤大致可以分为4步:

  • 步骤一:首先是画正N边形
  • 步骤二:从中心点到N边形各个顶点的连线
  • 步骤三:画战力区域
  • 步骤四:画战力值文字

步骤一:首先是要画一个正N边形,图例为正六边形:

画正N边形最重要的就是求出N边形的每个顶点座标,然后将这些顶点座标连接起来就可以了。

延伸一下:我们可以将问题转化为求圆周上的每个点的座标,首先要学习下Math.sin(弧度)、Math.cos(弧度),注意这里的参数是弧度而非角的度数。

弧度的计算公式为: 角度*(PI/180)

30° 角度 的弧度 = 30 * (PI/180)

如何得到圆上每个点的座标?

解决思路:根据三角形的正玄、余弦来得值;

假设一个圆的圆心座标是(a,b),半径为r,

则圆上每个点的: 
X座标=a + Math.sin(角度 * (Math.PI / 180)) * r ;
Y座标=b + Math.cos(角数 * (Math.PI / 180)) * r ;

例子:求时钟的秒针转动一圈的轨迹?

假设秒针的初始值(起点)为12点钟方向,圆心的座标为(a,b)。

解决思路:一分钟为60秒,一个圆为360°,所以平均每秒的转动角度为 360°/60 = 6°; 

for (int times = 0; times < 60; times++) {

       int hudu = 6 * (Math.PI / 180) *  times;

       int X = a + Math.sin(hudu) * r;

       int Y = b - Math.cos(hudu) * r    //  注意此处是“-”号,因为我们要得到的Y是相对于(0,0)而言的。

}

1、本例是以“12点为起点, 角度增大时为顺时针方向“,求X座标和Y座标的方法是:
X座标=a + Math.sin(角度 * (Math.PI / 180)) * r ;
Y座标=b - Math.cos(角数 * (Math.PI / 180)) * r ; 
 

2、一般“3点为起点, 角度增大时为逆时针方向“,求X座标和Y座标的方法是:
X座标 = a + Math.cos(角度 * (Math.PI / 180)) * r;    
Y座标 = b - Math.sin(角度 * (Math.PI / 180)) * r;    

明白了圆周上各个点的座标求法,下面看正N边形,各个顶点座标是怎么求的

六边形座标计算:

polygon

右上角的顶点座标:

x点座标:中心点 + R*sin(60°)

y点座标:中心点 - R*cos(60°)

整体代码:

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

import androidx.annotation.Nullable;

public class SpiderView extends View {

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    int width;
    int height;
    //N边形的边数
    int edges = 6;
    //根据边数求得每个顶点对应的度数
    double degrees = 360 / edges;
    //根据度数,转化为弧度
    double hudu = (Math.PI / 180) * degrees;

    {
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(3);
        paint.setColor(Color.BLACK);
    }

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

    public SpiderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
    }

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

        drawPolygon(canvas, 500.0F);
        drawPolygon(canvas, 400.0F);
        drawPolygon(canvas, 300.0F);
        drawPolygon(canvas, 200.0F);
        drawPolygon(canvas, 100.0F);
    }

    private void drawPolygon(Canvas canvas, float radius) {
        Path path = new Path();
        path.moveTo(width / 2, height / 2 - radius);//从上面的顶点出发

        float endx, endy;

        for (int i = 0; i < edges; i++) {
            endx = (float) (width / 2 + radius * Math.sin(hudu * i));
            endy = (float) (height / 2 - radius * Math.cos(hudu * i));
            path.lineTo(endx, endy);
        }
        path.close();
        canvas.drawPath(path, paint);
    }
}

上面代码中,只需要改变正N边形的边数edges即可。

步骤二:从中心点到各个顶点的连线

思路和画正N边形一样的,只是这里每画完一条射线都要将起始点挪动到原始点去,然后再画从原始点到各个顶点的下一条射线。

/**
     * 从中心点到各个顶点画一条线
     * @param canvas
     * @param radius
     */
    private void drawLines(Canvas canvas, float radius) {
        //从中心点出发
        Path path = new Path();
        path.moveTo(width / 2, height / 2);
        float endx, endy;
        for (int i = 0; i < edges; i++) {
            endx = (float) (width / 2 + radius * Math.sin(hudu * i));
            endy = (float) (height / 2 - radius * Math.cos(hudu * i));
            path.lineTo(endx, endy);
            canvas.drawPath(path, radialLinesPaint);
            //画完一条线后,重置起点在中心点,再画下一条直线
            endx = width/2;
            endy = height/2;
            path.moveTo(endx, endy);
        }
    }

步骤三:画战力区域

战力区域思路和画正N边形是一致的,只是这里每个点是半径的0~1的比率取值即可,这里通过rankData取值:

/**
     * 画战力值区域
     *
     * @param canvas
     * @param radius
     */
    private void drawRanks(Canvas canvas, float radius) {
        Path path = new Path();
        float endx, endy;
        for (int i = 0; i < edges; i++) {
            endx = (float) (width / 2 + radius * Math.sin(hudu * i) * rankData[i]);
            endy = (float) (height / 2 - radius * Math.cos(hudu * i) * rankData[i]);
            if (i == 0) {
                path.moveTo(width / 2, (float) (height / 2 - radius * 0.5));
            } else {
                path.lineTo(endx, endy);
            }
        }
        path.close();
        canvas.drawPath(path, rankPaint);
    }

步骤四:画战力值文字:

思路和上面两个一致,这里只是需要调整文字距离最外圈的距离:

/**
     * 画战力文字
     *
     * @param canvas
     * @param radius
     */
    private void drawRankText(Canvas canvas, float radius) {
        float endx, endy;
        Rect bounds = new Rect();
        for (int i = 0; i < edges; i++) {
            rankTextPaint.getTextBounds(rankText[i], 0, rankText[i].length(), bounds);
            endx = (float) (width / 2 + radius * 1.2 * Math.sin(hudu * i) - (bounds.right - bounds.left) / 2);
            endy = (float) (height / 2 - radius * 1.1 * Math.cos(hudu * i) + (bounds.bottom - bounds.top) / 2);
            canvas.drawText(rankText[i], endx, endy, rankTextPaint);
        }
    }

最后看下整体的实现效果,代码中改变下edges的数目即可变成正N边形:

战力值的所有代码:

package com.test.customview;

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

import androidx.annotation.Nullable;

public class SpiderView extends View {

    //正N边形边线
    Paint edgesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //中心发出的射线
    Paint radialLinesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //等级线
    Paint rankPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //等级文字
    Paint rankTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    //自定义view的宽高
    float width, height;
    //战力值数据
    private double[] rankData = {0.5, 0.2, 0.8, 0.6, 0.9, 0.6, 0.2, 0.8, 0.4, 0.9, 0.1, 0.7, 0.2, 0.9};

    //战力种类
    private String[] rankText = {"击杀", "助攻", "金钱", "物理", "防御", "魔法", "装备", "血量", "魔抗", "穿甲", "综合", "装甲", "魔抗"};

    //N边形的边数
    int edges = 7;
    //根据边数求得每个顶点对应的度数
    double degrees = 360 / edges;
    //根据度数,转化为弧度
    double hudu = (Math.PI / 180) * degrees;

    {
        edgesPaint.setStyle(Paint.Style.STROKE);
        edgesPaint.setStrokeWidth(3);
        edgesPaint.setColor(Color.BLACK);

        radialLinesPaint.setStyle(Paint.Style.STROKE);
        radialLinesPaint.setStrokeWidth(2);
        radialLinesPaint.setColor(Color.BLUE);

        rankPaint.setStyle(Paint.Style.STROKE);
        rankPaint.setStrokeWidth(10);
        rankPaint.setColor(Color.RED);

        rankTextPaint.setStyle(Paint.Style.FILL);
        rankTextPaint.setStrokeWidth(1);
        rankTextPaint.setColor(Color.BLACK);
        rankTextPaint.setTextSize(50);
    }

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

    public SpiderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
    }

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

        //正N边形个数
        edgesPaint.setStyle(Paint.Style.FILL);
        edgesPaint.setColor(Color.parseColor("#c3e3e5"));
        drawPolygon(canvas, 400.0F);
        edgesPaint.setColor(Color.parseColor("#85cdd4"));
        drawPolygon(canvas, 300.0F);
        edgesPaint.setColor(Color.parseColor("#48afb6"));
        drawPolygon(canvas, 200.0F);
        edgesPaint.setColor(Color.parseColor("#22737b"));
        drawPolygon(canvas, 100.0F);

        //从中心点到各个顶点的射线
        drawLines(canvas, 400);

        //画战力值区域
        drawRanks(canvas, 400);

        //画战力文字
        drawRankText(canvas, 400);
    }

    /**
     * 画战力值区域
     *
     * @param canvas
     * @param radius
     */
    private void drawRanks(Canvas canvas, float radius) {
        Path path = new Path();
        float endx, endy;
        for (int i = 0; i < edges; i++) {
            endx = (float) (width / 2 + radius * Math.sin(hudu * i) * rankData[i]);
            endy = (float) (height / 2 - radius * Math.cos(hudu * i) * rankData[i]);
            if (i == 0) {
                path.moveTo(width / 2, (float) (height / 2 - radius * 0.5));
            } else {
                path.lineTo(endx, endy);
            }
        }
        path.close();
        canvas.drawPath(path, rankPaint);
    }

    /**
     * 画战力文字
     *
     * @param canvas
     * @param radius
     */
    private void drawRankText(Canvas canvas, float radius) {
        float endx, endy;
        Rect bounds = new Rect();
        for (int i = 0; i < edges; i++) {
            rankTextPaint.getTextBounds(rankText[i], 0, rankText[i].length(), bounds);
            endx = (float) (width / 2 + radius * 1.2 * Math.sin(hudu * i) - (bounds.right - bounds.left) / 2);
            endy = (float) (height / 2 - radius * 1.1 * Math.cos(hudu * i) + (bounds.bottom - bounds.top) / 2);
            canvas.drawText(rankText[i], endx, endy, rankTextPaint);
        }
    }

    /**
     * 从中心点到各个顶点的射线
     *
     * @param canvas
     * @param radius
     */
    private void drawLines(Canvas canvas, float radius) {
        //从中心点出发
        Path path = new Path();
        path.moveTo(width / 2, height / 2);
        float endx, endy;
        for (int i = 0; i < edges; i++) {
            endx = (float) (width / 2 + radius * Math.sin(hudu * i));
            endy = (float) (height / 2 - radius * Math.cos(hudu * i));
            path.lineTo(endx, endy);
            canvas.drawPath(path, radialLinesPaint);
            //画完一条线后,重置起点在中心点,再画下一条直线
            endx = width / 2;
            endy = height / 2;
            path.moveTo(endx, endy);
        }
    }

    /**
     * 画正N边形
     *
     * @param canvas
     * @param radius
     */
    private void drawPolygon(Canvas canvas, float radius) {
        //从上面的顶点出发
        Path path = new Path();
        path.moveTo(width / 2, height / 2 - radius);

        float endx, endy;
        for (int i = 0; i < edges; i++) {
            endx = (float) (width / 2 + radius * Math.sin(hudu * i));
            endy = (float) (height / 2 - radius * Math.cos(hudu * i));
            path.lineTo(endx, endy);
        }
        path.close();
        canvas.drawPath(path, edgesPaint);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章