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邊形,各個頂點坐標是怎么求的
六邊形坐標計算:
右上角的頂點坐標:
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);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);width = w;height = h;}@Overrideprotected 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);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);width = w;height = h;}@Overrideprotected 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);} }?
總結
以上是生活随笔為你收集整理的Android画正N边形战力图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 适配器模式的三种形式
- 下一篇: Linux主机限速