用Kotlin畫起仿掌上英雄聯盟和懂球帝的自定義多邊形戰力圖

19snow93發表於2019-02-19

大家好,可能今年換了公司過得比較安逸,自己的心情也不算太好,所以一段時間都沒很好的學習和更新文章。自從最近看了扔物線HenCoder的文章之後,自己感覺自己對很多自定義View和動畫的知識都不太懂,甚至在平時的開發極少用到,所以自己想好好學習這一塊的知識。之前無意中在公眾號上看到一個仿英雄聯盟的的七邊形戰力圖,自己發現,平時我看的好幾個app都有這樣的多邊形戰力圖,所以覺得可以自己做一個大眾版的出來,適配各個關於多邊形戰力圖的自定義View。下面我們看看效果圖:

掌上英雄聯盟和懂球帝的效果圖.png
然後再看看我自己寫的自定義View的效果:
我自己寫的View效果圖.png
可以看到其實大致的效果是差不多的,但是有一些小細節可能需要自己去定製某個條件再處理,細化和潤色,因為我這裡是用同樣的方法,只是設定的引數不一樣,所以看上去有些細節需要再根據自己是幾邊形再調整一下。

大家看看我寫的程式碼就知道了:

配置程式碼.png
只需要短短的程式碼就能畫出不同的戰力多邊形。 好的,那下面我們就來講講這個東西是怎麼實現的,老規矩,如果不想聽分析的同學可以直接下載原始碼去看看。原始碼下載連結 講程式碼之前,先給大家講一下畫出這個多邊形的原理,對,就是數學原理,先看看下面的圖:
原理圖.png

用一個正六邊形舉例,假設說它每個角到座標原點為半徑X,那麼我們用數學的原理XcosA就能得出得出B點的X軸座標,XsinA能得出B點的Y軸座標,由此我們看出來B(XcosA,XsinA),至於∠A的度數怎麼來的就更簡單了,正n邊形360°/ n,即 2π/n 就能得到一個角是多少度的了。那很自然而然的,我們用一個for迴圈就可以得出六邊形所有的座標點

for (i in 0..count - 1) {
      //每個座標點
      (radius * cos(2 * Math.PI / count * i)).toFloat(), (radius * sin(2 * Math.PI / count * i)).toFloat())
 }
複製程式碼

以上就是多邊形戰力圖的數學原理,應該很簡單吧。其實很多自定義View也是一樣,只要懂了原理,很多東西很自然而然很簡單的就能寫出來。生活上很多事情也是一樣,沒做之前什麼事情都想著自己做不好就放棄了,但是自己嘗試了發現原來這個事情原來那麼簡單,我每次寫自定義View總有這個感慨。

不吹什麼了,確實畢業一年多工作兩年最近感慨有點。好了,下面我們來看看程式碼我是怎麼一步步實現的吧。在實現程式碼之前,我先說這個View我是用kotlin寫的,最近也慢慢用kotlin寫程式碼寫專案。一開始確實很不習慣,速度很慢,但是我覺得我總會熟能生巧的吧,所以貴在堅持嘛。 下面我們以懂球帝的六邊形戰力圖為例一步步畫出想要的效果吧:

一、首先我們需要畫出一個多邊形,和巢狀的多個多邊形。

首先我們需要畫出一個多邊形,和巢狀的多個多邊形.png

程式碼很簡單,我們看看紅色框裡面的程式碼就是最核心的部分,一個for迴圈,剛剛講原理的時候也說過。那下面詳細分析一下,當i=0的時候,我們把path移動到我們的第一個點上,

path.moveTo((radius * cos(2 * Math.PI / count * i - Math.PI / 2)).toFloat(), (radius * sin(2 * Math.PI / count * i - Math.PI / 2)).toFloat())
複製程式碼

當i>0的時候,我們就需要把這些點用路徑直線連起來了,所以用的是lineTo()

path.lineTo((radius * cos(2 * Math.PI / count * i - Math.PI / 2)).toFloat(), (radius * sin(2 * Math.PI / count * i - Math.PI / 2)).toFloat())
複製程式碼

有人會問為什麼角度最後需要減去π / 2,那是因為我們需要把六邊形旋轉一下,視覺效果更好。

一個六邊形.png
一個六邊形我們畫出來了,那麼一個六邊形裡面多個巢狀的多邊形就能很容易的畫出來了,只需要在第一個for迴圈的外面再加一個for迴圈,每次把半徑都減去一定的值,那很簡單就畫出來了。在上圖中紅色框框外的程式碼做的事情就是去加一個for迴圈,和給畫筆paint加上一些圖片的填充顏色,另外還在六邊形描了畫邊。
多邊形巢狀.png

二、畫六邊形的中心到每個角的角邊:

有人注意到了嗎,我們在第一個for迴圈的時候加了一句程式碼:

//把相應的點儲存到pointList中
points.add(PointF(radius * cos(2 * Math.PI / count * i - Math.PI / 2).toFloat(), radius * sin(2 * Math.PI / count * i - Math.PI / 2).toFloat()))
複製程式碼

這句程式碼就是把我們所要畫的六邊形的六個點的座標儲存了下來,為方便我們之後用,而且下面很多地方都用到了,你看,這裡就用到了。

畫角邊.png
我們這裡考慮到有些多邊形不需要角邊,所以加了一個判斷,不需要就設定false就好了。
畫角邊圖.png
畫完角邊我們就考慮一下多邊形外面的字型了。

三、畫出字型

畫出字型程式碼.png
老實說,這裡是我做這個自定義View最難受的地方,因為字型的位置要根據六邊形每個角慢慢調整字型的不同的位置,我沒想到更好的辦法處理,暫時只是按照座標軸的四個象限來稍微調整了一下位置,所以當每個同學有興趣做定製化的時候,需要自己再做一些細節的字型位置調整處理。
畫出字型圖.png
畫完六邊形外面的內容,那麼我們就要往裡看看了吧。

四、畫出分數區域及設定相關屬性

1.我們要設定分數區域的一些屬性,另它更好看一點:

設定分數區域屬性.png
2.設定各個屬性的分數比例:
設定各個屬性的分數比例.png
3.按比例畫出每個區域的點並連成線:
按比例畫出每個區域的點並連成線.png
其實這一步跟第一步很類似,也並沒有什麼難點可言。 最後我們來看看最終的效果圖:
最終的效果圖.png
講完多邊形戰力圖的原理程式碼,我們再看看要如何使用:
使用圖.png
關於使用,我用了設計模式的builder模式,即建造者模式來管理整個自定義View的配置。我把每個屬性都分開做定製化處理,並連成一條資料鏈初始化,然後最後呼叫build的方法就可以實現所有方法的配置,是不是很簡便,雖然程式碼量多了,但是builder模式對我以後對這個自定義View的修改和擴充套件非常有好處。

下面講講做這個自定義View在kotlin上遇到的一些小問題:

1.建構函式的編寫跟java有區別:

建構函式的編寫跟java有區別.png
有人注意到了嗎,雖然在意思上是一樣的,但是kotlin和java在表達上在建構函式這塊上可有不一樣的地方。

2.kotlin的小坑:

誰都知道,kotlin是不需要在程式碼中初始化xml控制元件的,這一點用過的人都會覺得很方便,但是在這裡我踩了一個小坑,看下圖:

kotlin初始化.png
kotlin_xml.png
大家是不是會覺得這段程式碼完全沒問題?我一開始也是這麼認為的,但是我得到的結果是。。。。。
image.png
為什麼說我沒有初始化呢,明明程式碼的編譯成功了,我一開始想不明白為什麼,後來經過一個師兄的提醒 ,我才發現我們在宣告的時候,dongqiudiPolygonView指向的只是一個int型別的數字,但是如果你想用他的build方法,你還是要老老實實的加上一句

dongqiudiPolygonView = findViewById(R.id.dongqiudiPolygonView)
複製程式碼

findViewById.png
哎,坑啊,所以說探索還是需要有點代價的,下次就不會再犯這種低階錯誤了。 好了,這一期就講得差不多了,如果對我的程式碼有什麼建議的同學可以跟我討論一下,譬如說很多kotlin的問題我自己都沒搞得懂~對我的專案有興趣的同學可以下載我的原始碼一起學習和交流,共同進步!原始碼下載連結

最近在簡書感覺漸漸沒人看了,所以第一次在掘金髮布文章,如果大家想看我的文章,我可以把那邊的文章遷移到這邊來。 我的簡書

相關文章