数据可视化能力图分析,包含了多边形能力图:四边形,五边形,六边形,七边形,八边形等等能力图
可视化图表系列如下:
(一)Compose曲线图表库WXChart,你只需要提供数据配置就行了
(二)Compose折线图,贝赛尔曲线图,柱状图,圆饼图,圆环图。带动画和点击效果
(三)全网最火视频,Compose代码写出来,动态可视化趋势视频,帅到爆
(四)全网最火可视化趋势视频实现深度解析,同时新增条形图表
(五)庆元旦,出排名,手撸全网火爆的排名视频,排名动态可视化
(六)Android六边形战士能力图绘制,Compose实现
(七)Android之中美PK,赛事PK对比图Compose实现
(八)Android之等级金字塔之Compose智能实现
以下四边形
游戏中某战力图:
以下是掘金掘友分五边形
社区图:
以下可以是游戏中某个人物能力八边形
图:
前言
对于多边形能力图,可以可视化很直观的就能分析某些能力属性上是否均衡,是否有偏科现象,比如:
可以很直观分析学生成绩是否偏科
可以很直观看出NBA球员能力是否均衡:
以下是NBA官方数据 约基奇
能力图:
作为纯Android开发
,假设有下边这五项技能,我们的每一项技能研究得有多深呢?
一、数据模型设计
1. PolygonModel:设计为绘制的数据模型
data class PolygonModel(
val num: Int,// N变形里面有几层背景
val list: MutableList<PolygonBean>, //N边形 数据 list.size为多少N值为多少
val max: Float, //标准总分多少,比如纯Android开发,这五项技能力值最大为100,
val radiusOffset: Float = 80f, //视图长款各一半最小的那个座位最大半径,减去该值后就是绘制的N变形半径
var color: Color = Color(0x30ACAA00),//背景填充颜色
val valueColor: Color = Color(0x50FF0000),//值填充背景颜色
val valueLineColor: Color = Color(0xFFFF0000)//值外层线条颜色
) : ChartBaseModel()
2. ChartBaseModel的基础类里面只用到以下几个属性
open class ChartBaseModel {
var offsetx: Float = 10f//原点 x偏移,正数 视图中心右偏移,负数左偏移
var offsety: Float = 10f //原点 y 偏移 正数 视图中心下偏移,负数上偏移
var durationMillis: Int = 1000 // 动画时长
var animateDelay: Long = 1000 //动画延迟执行时间
}
3. PolygonBean为每一项能力的属性,title为属性名称,value为属性值
data class PolygonBean(val title: String, val value: Float)
二、示例数据提供和调用:
1、repositories
中添加如下maven
repositories {
maven { url 'https://repo1.maven.org/maven2/' }
maven { url 'https://s01.oss.sonatype.org/content/repositories/releases/' }
}
}
2、 dependencies
中添加依赖
implementation("io.github.wgllss:Wgllss-WXChart:1.0.16")
3. 在android的ViewModel中提供数据
具体业务数据可以从网络获取,获取完了造成以下配置模型
class PolygonViewModel : ViewModel() {
private val _datas = MutableLiveData<PolygonModel>()
val polygonModel: LiveData<PolygonModel> = _datas
fun setData() {
_datas.value = PolygonModel(
5, //背景层N变形从小到大个数
mutableListOf(
PolygonBean("应用层UI", 95f),
PolygonBean("Framework", 81f),
PolygonBean("性能优化", 94f),
PolygonBean("NDK音视频", 88f),
PolygonBean("架构", 80f),
// PolygonBean("魔法", 58f),
// PolygonBean("装备", 78f),
// PolygonBean("血量", 98f),
// PolygonBean("政治", 75f),
// PolygonBean("生物", 89f),
// PolygonBean("地理", 69f),
// PolygonBean("篮板", 89f),
// PolygonBean("篮板", 89f),
), 100f,//满分多少
toDp(80f), //最大半径偏移
color = Color(0x30EEAA00),//背景填充颜色
valueColor = Color(0x50FF0000),//值填充颜色
valueLineColor = Color(0xFFFF0000)//值外层线条颜色
)
}
fun toDp(size: Float) = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, MyApp.application.resources.displayMetrics)
}
4. Composes使用方调用端如下:
真正绘制N边形战力图方法是 drawPolygonChart
传入的TextStyle 可以配置各项指标熟悉文字颜色,大小 等
@Composable
fun polygonChart(polygonViewModel: PolygonViewModel) {
val textMeasurer = rememberTextMeasurer()
val chatModel by polygonViewModel.polygonModel.observeAsState()
chatModel?.let {
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
val modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(Color.White)
//TextStyle 可以配置各项指标熟悉文字颜色,大小 等
drawPolygonChart(
modifier = modifier, textMeasurer, TextStyle(
fontSize = 20.sp, fontWeight = FontWeight.Normal, color = Color.Black
), it
)
}
}
}
三、N边形战力图真正绘制实现
真正绘制借助于Compose的 Canvas 和drawPath方法,只需要计算出N变形各个点的位置X,Y坐标就可以了。
全是初中数学中几何知识
- 先根据数据个数计算出N变形的弧度,然后转化成角度:
val degrees = 360 / it.list.size
val radians = degrees * (PI / 180)
- 角度大小依次为:
radians * k
: k从0到list.size-1 - 每个点的
X值
为:centerX + distence
Y值
为:centerY - radius * cosValue
如下面计算得出:
val cosValue = cos(radians * k).toFloat()
val sinValue = sin(radians * k).toFloat()
val distence = radius * sinValue
path.lineTo(centerX + distence, centerY - radius * cosValue)
- 具体实现如下:
@Composable
fun drawPolygonChart(modifier: Modifier, textMeasurer: TextMeasurer, style: TextStyle, it: PolygonModel) {
val context = LocalContext.current
val fontSizeDip = DisplayUtil.sp2Dip(context, style.fontSize.value)
var mSize by remember { mutableStateOf(Size(0f, 0f)) }
val centerX = mSize.center.x
val centerY = mSize.center.y
val radiusMax = min(centerX - it.radiusOffset, centerY - it.radiusOffset) //饼图半径
val degrees = 360 / it.list.size
val radians = degrees * (PI / 180)
val abs = radiusMax / it.max
var animatedloadPos by remember { mutableStateOf(0) }
val animatedValue by animateFloatAsState(targetValue = if (animatedloadPos == 0) 0f else 1f, animationSpec = tween(it.durationMillis))
LaunchedEffect(Unit) {
delay(it.animateDelay)
animatedloadPos = 1
}
Canvas(modifier) {
mSize = size
val path = Path()
//绘制背景一层一层N边形
for (i in 0 until it.num) {
val radius = (i + 1) * (radiusMax / it.num)
path.moveTo(centerX, centerY - radius)
for (k in 1 until it.list.size) {
val cosValue = cos(radians * k).toFloat()
val sinValue = sin(radians * k).toFloat()
val distence = radius * sinValue
path.lineTo(centerX + distence, centerY - radius * cosValue)
}
path.lineTo(centerX, centerY - radius)
drawPath(path = path, color = it.color, style = Fill)
path.reset()
}
//绘制当前能力值N边形
path.moveTo(centerX, centerY - abs * it.list[0].value * animatedValue)
for (i in 1 until it.list.size) {
val cosValue = cos(radians * i).toFloat()
val sinValue = sin(radians * i).toFloat()
val radius = abs * it.list[i].value * animatedValue
val distence = radius * sinValue
path.lineTo(centerX + distence, centerY - radius * cosValue)
}
path.lineTo(centerX, centerY - abs * it.list[0].value * animatedValue)
drawPath(path = path, color = it.valueColor, style = Fill)
drawPath(path = path, color = it.valueLineColor, style = Stroke(width = 2f))
path.close()
//绘制当前N变形各个点上面的值 文字数字
for (i in 0 until it.list.size) {
val cosValue = cos(radians * i).toFloat()
val sinValue = sin(radians * i).toFloat()
val distence = radiusMax * sinValue
val title = "${it.list[i].title}: ${String.format("%.0f", it.list[i].value)}"
val dl = title.length * fontSizeDip
if (sinValue == 0.0f || cosValue == 1.0f) {
drawText(textMeasurer = textMeasurer, style = style, text = title, topLeft = Offset(centerX - dl / 2, max(0f, centerY - radiusMax - 4 * fontSizeDip)))
} else if (sinValue == 0.0f || cosValue == -1.0f) {
drawText(textMeasurer = textMeasurer, style = style, text = title, topLeft = Offset(centerX - dl / 2, min(size.height, centerY + radiusMax + 3 * fontSizeDip)))
} else if (distence > 0f) {
drawText(textMeasurer = textMeasurer, style = style, text = title, topLeft = Offset(centerX + distence + 3 * fontSizeDip, centerY - radiusMax * cosValue - fontSizeDip))
} else if (distence < 0f) {
drawText(textMeasurer = textMeasurer, style = style, text = title, topLeft = Offset(centerX + distence - dl - 3 * fontSizeDip, centerY - radiusMax * cosValue - fontSizeDip))
}
}
}
}
总结
本文重点介绍了怎么用Compose绘制出N变形战力图
已经封装成库,你只需要准备好数据就可以了。