今天看啥  ›  专栏  ›  19snow93

用Kotlin画起仿掌上英雄联盟和懂球帝的自定义多边形战力图

19snow93  · 掘金  ·  · 2017-12-19 02:21

大家好,可能今年换了公司过得比较安逸,自己的心情也不算太好,所以一段时间都没很好的学习和更新文章。自从最近看了扔物线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的问题我自己都没搞得懂~对我的项目有兴趣的同学可以下载我的源码一起学习和交流,共同进步!源码下载链接

最近在简书感觉渐渐没人看了,所以第一次在掘金发布文章,如果大家想看我的文章,我可以把那边的文章迁移到这边来。 我的简书




原文地址:访问原文地址
快照地址: 访问文章快照