MPAndroidChart setLabelRotationAngle bug
MPAndroidChart setLabelRotationAngle bug(应该包括其他关于修改x/y label的bug)
库是好库,但就是不更新了。。
bug 描述:修改 labelRotationAngle
(setLabelRotationAngle()
),手势缩放一下视图,才触发自动调整视图,会导致图表大小发生变化。用起来就是,第一次显示,label被截断,必须手动缩放一下图表,才能触发视图自适应。
解决过程
上 github 搜一下,也是一个已知陈年老 bug:https://github.com/PhilJay/MPAndroidChart/issues/3298#issuecomment-442241286
谷歌翻译:我尝试旋转标签
xAxis.setLabelRotationAngle(-30f)
; 这有帮助,但对于第一次渲染它被裁剪,我必须缩放图表以重新渲染,然后显示完整的标签。不知道如何强制初始重新渲染。
继续 github 上看了一个 pr,感觉改的也不好。google 了一圈,没有找到一个很好的不改动库源代码解决方法。
于是自己摸索了下:全文无图(懒
先知道一下某几个方法的意义(基于折线统计图):
mXAxisRenderer.computeAxis()
:涉及计算 xLabel 的宽高:mXAxis.mLabelRotatedHeight
、mXAxis.mLabelRotatedWidth
BarLineChartBase.calculateOffsets()
:涉及计算图表的宽高信息等ViewPortHandler.mContentRect
。
调试了下,得到原先大致逻辑:
setDate()
,则notifyDataSetChanged()
,触发一次计算mXAxisRenderer.computeAxis()
、calculateOffsets()
;onDraw
又触发一次calculateOffsets()
、mXAxisRenderer.computeAxis()
直接绘制出 x、y 轴,以及上面的 label 标签;- 然后是绘制图上的点,省略。
总结原因:当我们设置 data 的时候,会立即直接计算出对应的 label 宽高信息,而后续我们再设置 xAxis 等轴信息的时候就无法参与计算。导致最后绘图异常。
放大触发原因
手动放大 onTouch
会触发 mChart.calculateOffsets()
,该方法会更新 ViewPortHandler.mContentRect
更新 bottom,随后再触发 onDraw
。
点击不触发原因
点击触发 BarLineChartBase.onDraw
-> mXAxisRenderer.computeAxis
更新计算 mXAxis.mLabelRotatedHeight
、mXAxis.mLabelRotatedWidth
;(加上bottom)
并画标签:mXAxisRenderer.renderAxisLabels(canvas)
;
好像没有重新计算 bottom 重绘啊,忘记了,笔者写此文时也没有验证,以上摘自草稿记录
解决方案
既然知道大致逻辑,那就很好办了。
我们直接先设置好轴信息,再 setDate()
即可!!!
本来是只想先把
setLabelRotationAngle()
(即lineChart.xAxis.labelRotationAngle
),放在setData()
前面。测试发现高度最终会有些许出入,表现出来也就是旋转后的 label 会有部分被切掉,也就是展示高度虽有变高,但仍不够。
一想,我们对轴做了那么多自定义,那么这个误差也是理所当然的。
// kotlin举例
fun constructChart(analyzedData: AnalyzedData) {
lineChart.tag = analyzedData
lineChart.description.text = "数据图表"
lineChart.description.yOffset = 22f
lineChart.description.textColor = colorBoarder
lineChart.setNoDataText("暂无数据")
lineChart.setNoDataTextColor(primaryColor)
val xCount = 999f
// 设置 X 轴
val xAxis = lineChart.xAxis
xAxis.axisMaximum = xCount
xAxis.axisMinimum = 0f
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.setDrawAxisLine(false)
xAxis.setDrawGridLines(false)
// 设置数量
xAxis.setLabelCount(xCount.toInt())
xAxis.valueFormatter = analyzedData
xAxis.textColor = colorBoarder
xAxis.isGranularityEnabled = true
xAxis.setGranularity(1f)
lineChart.xAxis.labelRotationAngle = when {
xCount > 25f -> -90f
xCount > 13f -> -45f
else -> 0f
}
// 设置 Y 轴
val yRightAxis = lineChart.axisRight
yRightAxis.axisMaximum = analyzedData.ceilMaxY
yRightAxis.axisMinimum = 0f
LogUtils.log("YAxisMaximum: ${yRightAxis.axisMaximum}")
yRightAxis.setDrawGridLines(true)
yRightAxis.enableGridDashedLine(15f, 5f, 0f)
yRightAxis.textColor = colorBoarder
yRightAxis.isGranularityEnabled = true
yRightAxis.setGranularity(1f)
val yLeftAxis = lineChart.axisLeft
yLeftAxis.axisMaximum = analyzedData.ceilMaxY
yLeftAxis.axisMinimum = 0f
yLeftAxis.setDrawLabels(false)
yLeftAxis.setDrawGridLines(false)
lineChart.animateXY(600, 600, Easing.EaseInSine, Easing.EaseInSine)
// 设置图例
val legend: Legend = lineChart.legend
legend.isEnabled = false
// 最后设置数据,第一次设置时也会触发notifyDataSetChanged,重新计算
lineChart.data = analyzedData.lineData
}
最后,此外如果自动计算并没有全部显示 label,可以尝试设置额外 offset
lineChart.setExtraOffsets(10f, 80f, 10f, if (xCount > 25) 10f else 0f)
写文章的时候,没有再调试了,全凭草稿写的,图都懒得弄。仅做一个记录。可能有所纰漏,欢迎指出。
或许可以尝试:https://github.com/AppDevNext/AndroidChart