图片
实现过程
由于我初次看文章时并没有看的太细,感觉这个效果很炫酷就上手做了,完成之后仔细对比各个细节的实现,发现是有一些不同的,不过整体的思路大致是一致的,最后还有彩蛋。
准备工作
创建Compose方法,确定参数
新建kotlin文件,输入comp,回车,AS帮我们自动生成compose方法的模板,起个名字就叫SpiderWebRadarLineDiagram(蛛网雷达折线图)。
/**
* @param modifier 修饰符
* @param dataList 需要绘制的数据列表
* @param labelList 数据列表对应的标签
* @param layerNum 绘制蛛网的层数
* @param maxData_ 最外层蛛网代表的最大值,为空则取 dataList 中的最大值
*/
@Composable
fun SpiderWebRadarLineDiagram(
modifier: Modifier,
dataList: List<Float>,
labelList: List<String>,
layerNum: Int = 5,
maxData_: Float? = null
) {
//数据长度和标签长度判断处理,若不相等或为空抛出异常
if (dataList.size != labelList.size || dataList.isEmpty()) {
throw IllegalArgumentException(“dataList.size can not be empty,and it must equals to paramList.size!“)
}
//计算数据长度,用于确定绘制几边形
val count = dataList.size
//确定最外层代表的数值上限
val maxData = maxData_ ?: dataList.max()
//TODO 绘制
}
我们绘制折线图,需要确定图的大小、数据、标签、网的层数、最大值等,这些基本信息作为参数支持调用者自由定义,后续可以把线的粗细、颜色、字体大小等都提取成参数(AS MAC上智能提取参数快捷键:option + command + P)。另外对参数的输入做合法校验和处理。
添加Canvas
在上面的蛛网图方法中的TODO 正文中添加Canvas组件,并传入modifier,Canvas内部就是我们控制绘制的地方了
@Preview(showBackground = true)
@Composable
fun SpiderWebRadarLineDiagramPreview() {
SpiderWebRadarLineDiagram(
modifier = Modifier.fillMaxSize(),
dataList = listOf(4f, 5f, 5f, 4f, 5f),
labelList = listOf(“德“, “智“, “体“, “美“, “劳“)
)
}
编写Preview代码,实时预览
输入prev,回车,生成Preview模版,起名 SpiderWebRadarLineDiagramPreview,调用我们刚才创建的蛛网图方法。
@Preview(showBackground = true)
@Composable
fun SpiderWebRadarLineDiagramPreview() {
SpiderWebRadarLineDiagram(
modifier = Modifier.fillMaxSize(),
dataList = listOf(4f, 5f, 5f, 4f, 5f),
labelList = listOf(“德“, “智“, “体“, “美“, “劳“)
)
}
这里将showBackground设为true,显示背景色可以更加接近真实的效果,传入modifier,填满整个布局,传入绘制的数据,build一下,就可以看到,一片空白,因为我们还没有绘制任何东西呢。
绘制任意多边形
绘制辅助圆
根据Canvas当前尺寸宽高的较小者,并预留5%的边距空白,确定辅助圆的半径(预留空白用于后续绘制标签文本)。
Canvas(modifier = modifier) {
//计算多边形相接圆的半径
val radius = java.lang.Float.min(size.height, size.width) * 0.45f
//画辅助圆
drawCircle(Color.Cyan, radius, center)
}
图片
计算顶点坐标
我们前面根据调用者传递进来的dataList的size,确定了需要绘制的多边形的边数 count,根据count计算出各个顶点分布的角度,结合辅助圆的半径,利用高中学过的 sin 和 cos 三角函数知识,计算出x,y的值,注意方向和正负,这里还涉及到角度转换成弧度制,使用Math.toRadians()。
/**
* 根据角度计算坐标
*
* @param rotation 角度
* @param radius 半径
*/
private fun DrawScope.calculateXY(
rotation: Float,
radius: Float
): Pair<Float, Float> {
//将角度单位转换,如180度转换成Pi
val radian = Math.toRadians(rotation.toDouble())
return calculateXYByRadian(radian, radius)
}
/**
* 根据弧度计算坐标
*
* @param radius 半径
* @param radian 弧度
*/
private fun DrawScope.calculateXYByRadian(
radian: Double,
radius: Float
): Pair<Float, Float> {
val x = (radius * cos(radian) + center.x).toFloat()
val y = (radius * sin(radian) + center.y).toFloat(&#