Android 能力圖實現
效果
國際慣例,效果奉上

Screenshot_1548296212_看圖王.png
思路
- 首先確定的是所有的引數,三個畫筆(畫線,畫圖,畫字)
- 確定畫出的形狀引數,幾邊形、幾層圖形(上圖一共為4層顯示)、最外圈的半徑、每層圖形的角度和半徑就可以算出來了;
- 確定該如何畫出點(也就是能力值)
- 最後繪製文字
- Tips: 好的封裝可以實現自定義設定View的各種屬性;
實現
-
確定引數
在建構函式中實現了三個方法,分別初始化相應的引數
public class AibilitysView extends View { private Paint linePaint;//畫線的筆 private Paint textPaint;//畫文字的筆 private Object[] allAbility; private int n;//邊的數量或者能力的個數 private float intervalCount;//間隔數量,把半徑分為幾段 private float R; //最外圈的半徑 private float angle; //角度 private int viewHight;//控制元件的高度 private int viewWidth;//控制元件的寬度 private ArrayList<ArrayList<PointF>> pointArrayList;//儲存多邊形頂點陣列的陣列 private ArrayList<PointF> abilityPoints;//儲存能力點的陣列 public AibilitysView(Context context) { this(context, null); } public AibilitysView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public AibilitysView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initSize(); initPoints(); initPaint(); } *** }
-
initSize()
方法先初始化了相關SIze
引數(預設的引數為 10 個邊,最外層的半徑長度為100dp
)/** * 初始化固定資料Size * 設定了是幾邊型; * 設定了最外圈的半徑 * 計算出每一邊型的角度 * 獲取螢幕方向 */ private void initSize() { if (allAbility == null) { n = 10;//十條辺 } else { n = allAbility.length; } R = dp2pxF(getContext(), 100); intervalCount = 4; //有四層 angle = (float) ((2 * Math.PI) / n); //2π是一週,除以n是算出平均每一個的角度是多少 /** * * 此方法預設獲取的是整個手機的寬度和高度;無論如何調節控制元件或父控制元件的寬度都不可能實現,Size的改變 * * //拿到螢幕的寬高 int screenWidth = getResources().getDisplayMetrics().widthPixels; //控制元件設定為正方向 viewHight = screenWidth; viewWidth = screenWidth; */ }
-
initPoints()
方法初始化點的位置(通過計算算出點的位置)/** * 初始化點的位置 */ private void initPoints() { if (pointArrayList == null) { pointArrayList = new ArrayList<>(); } else { pointArrayList.clear(); } float x; float y; for (int i = 0; i < intervalCount; i++) { //建立一個儲存點的陣列 ArrayList<PointF> points = new ArrayList<>(); for (int j = 0; j < n; j++) { float r = R * ((float) (intervalCount - i) / intervalCount);//每一圈的半徑按比例減少 //這裡減去Math.PI /2 是為了讓段變形逆時針旋轉90度,所以後面的所有用到cos,sin的都要減 x = (float) (r * Math.cos(j * angle - Math.PI / 2)); y = (float) (r * Math.sin(j * angle - Math.PI / 2)); points.add(new PointF(x, y)); } pointArrayList.add(points); } }
-
initPaint()
方法初始化了畫筆/** * 初始化畫筆 */ private void initPaint() { //畫線的筆 linePaint = new Paint(Paint.ANTI_ALIAS_FLAG); //設定線筆的寬度 linePaint.setStrokeWidth(dp2px(getContext(), 1f)); //畫文字的筆 textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //設定文字居中 textPaint.setTextAlign(Paint.Align.CENTER); textPaint.setColor(Color.BLACK); textPaint.setTextSize(sp2pxF(getContext(), 14f)); }
-
-
確定位置 通過繼承View ,可重寫相應的方法
onMeasure()
和onSizeChanged()
方法;如果View中的Size改變的話,會重新初始化
initSize()
和initPoints()
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); viewWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); viewHight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); //設定控制元件的最終檢視大小 setMeasuredDimension(viewWidth, viewHight); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); initSize(); initPoints(); }
-
重寫
OnDraw()
方法,開始畫圖@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //把畫布的原點移動到控制元件的中心點 canvas.translate(viewWidth / 2, viewHight / 2); //繪製形狀 drawPolygon(canvas); //畫出邊框線 drawOutLine(canvas); //畫出能力線 drawAbilityLine(canvas); //畫出文字 drawAbilityText(canvas); }
-
在畫布上畫出形狀
/** * 在畫布上繪製畫出的形狀 * * @param canvas */ private void drawPolygon(Canvas canvas) { //儲存畫布當前狀態(平移,縮放、旋轉、裁剪)和canvas.restore()配合使用 canvas.save(); //填充且描邊 linePaint.setStyle(Paint.Style.FILL_AND_STROKE); //設定路徑 Path path = new Path(); //迴圈、一層一層的繪製 for (int i = 0; i < intervalCount; i++) { //每一層的顏色更改 switch (i) { case 0: linePaint.setColor(Color.parseColor("#D4F0F3")); break; case 1: linePaint.setColor(Color.parseColor("#99DCE2")); break; case 2: linePaint.setColor(Color.parseColor("#56C1C7")); break; case 3: linePaint.setColor(Color.parseColor("#278891")); break; } //每一層都有n個點 for (int j = 0; j < n; j++) { float x = pointArrayList.get(i).get(j).x; float y = pointArrayList.get(i).get(j).y; if (j == 0) { //如果是每層的第一個點,就把它設定為path的起始點 path.moveTo(x, y); } else { path.lineTo(x, y); } } //關閉路徑 path.close(); //在畫布上畫出路徑 canvas.drawPath(path, linePaint); //清除Path儲存的路徑 path.reset(); } canvas.restore(); }
-
畫出邊框線
/** * 繪製多邊形的辺,輪廓線 * * @param canvas */ private void drawOutLine(Canvas canvas) { //遇上一個方法中的用意一樣 canvas.save(); //設定畫筆的顏色 linePaint.setColor(Color.parseColor("#99DCC2")); //設定畫筆的樣式為空心 linePaint.setStyle(Paint.Style.STROKE); //先畫出最外面的多邊形輪庫 Path path = new Path(); for (int i = 0; i < n; i++) { //只需要第一組的點 float x = pointArrayList.get(0).get(i).x; float y = pointArrayList.get(0).get(i).y; if (i == 0) { //如果是第一個點就把path的起點設定為這個點 path.moveTo(x, y); } else { path.lineTo(x, y); } } //閉合路徑 path.close(); canvas.drawPath(path, linePaint); //再畫頂點到中心的線 for (int i = 0; i < n; i++) { float x = pointArrayList.get(0).get(i).x; float y = pointArrayList.get(0).get(i).y; canvas.drawLine(0, 0, x, y, linePaint); //起點都是中心點 } canvas.restore(); }
-
畫出能力線
/** * 畫出能力線 * * @param canvas */ private void drawAbilityLine(Canvas canvas) { if (allAbility == null) { return; } canvas.save(); //先把能力點初始化出來 abilityPoints = new ArrayList<>(); for (int i = 0; i < n; i++) { float r = R * (Float.valueOf("" + allAbility[i]) / 100.0f);//能力值/100再乘以半徑就是所佔的比例 float x = (float) (r * Math.cos(i * angle - Math.PI / 2)); float y = (float) (r * Math.sin(i * angle - Math.PI / 2)); abilityPoints.add(new PointF(x, y)); } linePaint.setStrokeWidth(dp2px(getContext(), 2f)); linePaint.setColor(Color.parseColor("#E96153")); linePaint.setStyle(Paint.Style.STROKE);//設定空心的 Path path = new Path();//路徑 for (int i = 0; i < n; i++) { float x = abilityPoints.get(i).x; float y = abilityPoints.get(i).y; if (i == 0) { path.moveTo(x, y); } else { path.lineTo(x, y); } } path.close();//別忘了閉合 canvas.drawPath(path, linePaint); canvas.restore(); }
- 畫出文字
private void drawAbilityText(Canvas canvas) { canvas.save(); ArrayList<PointF> textPoints = new ArrayList<>(); for (int i = 0; i < n; i++) { float r = R + dp2pxF(getContext(), 15f); float x = (float) (r * Math.cos(i * angle - Math.PI / 2)); float y = (float) (r * Math.sin(i * angle - Math.PI / 2)); textPoints.add(new PointF(x, y)); } //拿到字型測量器 Paint.FontMetrics metrics = textPaint.getFontMetrics(); for (int i = 0; i < n; i++) { float x = textPoints.get(i).x; //ascent:上坡度,是文字的基線到文字的最高處的距離 //descent:下坡度,,文字的基線到文字的最低處的距離 float y = textPoints.get(i).y - (metrics.ascent + metrics.descent) / 2; canvas.drawText(allAbility[i] + "", x, y, textPaint); } canvas.restore(); }
-
-
其他
/** * 傳入元資料 * * @param data */ public void setData(Object[] data) { if (data == null) { return; } this.allAbility = data; //View本身呼叫迫使view重畫 invalidate(); } /** * 下面都是工具類,dp單位轉px單位 */ public static int dp2px(Context c, float dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, c.getResources().getDisplayMetrics()); } public static int sp2px(Context c, float sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, c.getResources().getDisplayMetrics()); } public static float dp2pxF(Context c, float dp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, c.getResources().getDisplayMetrics()); } public static float sp2pxF(Context c, float sp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, c.getResources().getDisplayMetrics()); }
使用
自定義View的功能實現完畢,那麼就看看該怎麼使用了;
- XML主頁檢視
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.example.administrator.abilityview.view.AibilitysView android:id="@+id/aibilitymapview" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
- MainActivity 實現
package com.example.administrator.abilityview; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.example.administrator.abilityview.view.AibilitysView; public class MainActivity extends AppCompatActivity { private AibilitysView aibilitymapview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.aibilitymapview = (AibilitysView) findViewById(R.id.aibilitymapview); aibilitymapview.setData(new Object[]{65, 70, 80, 70, 80, 80 }); } }
寫在最後
歡迎大家評論吐槽,多給點建議,我會給大家解答的;跪謝!!!!
GitHUb 地址: https://github.com/sikeziji/AbilityView ;希望大家可以去Star;感謝