我有一个应用程序,用户可以选择日期(从/到),货币列表,然后,显示图表与一些数据。这个图表,只是堆积的条形图,从MPAndroidChart
开始。最近,我实现了SwiperRefreshLayout
,因此用户可以进行不止一个API调用。显示图表有两种情况:
**首先(一切正常)**当用户将选择例如2种货币时,他可以进行另一次调用,但他不能超过第一次选择的货币数量。所以在这种情况下,他可以选择一种或两种货币。不管是哪一个。
第二,程序崩溃用户将选择X数量的货币,图表将显示,刷新布局后,用户将选择X+1货币。这使得程序崩溃。
由于我已经花了几天时间处理这个问题,我试图清除数据,使图表无效等。事实证明,这个命令使程序崩溃,
mBinding.myChart.setCustom(myEntries)
我可以运行整个程序没有它,但它是必要的,因为它提供了标签的图表。
下面是一些代码:
全局变量
private var myChartData: MutableList<BarEntry> = mutableListOf()
private var set: BarDataSet? = null
API调用
private val apiCall: Job
get() =
viewLifecycleOwner.lifecycleScope.launch(start = CoroutineStart.LAZY) {
for (i in 0 until pickedCurrs.size) {
myPickedCurrencies += "${pickedCurrs[i]}, "
}
mViewModel.fetchData(
myBaseCurrency,
myPickedCurrencies,
fromDate,
toDate
)
mViewModel.myApiData.observe(viewLifecycleOwner, Observer { status ->
when (status) {
is MyWrapper.Success -> {
status.data?.timeSeriesRates?.entries?.forEachIndexed { index, entry ->
myChartData.add(
BarEntry(
index.toFloat(),
entry.value.values.map { it.toFloat() }.toFloatArray()
)
)
}
makeChart(
myChartData,
formatDate(status.data?.timeSeriesRates?.keys!!.toList()),
mSelectedCurrencies
)
}
is MyWrapper.Error -> {
Log.i(TAG, "onCreateView: ERROR")
}
}
})
}
makeChart
private fun prepareChart(
entries: MutableList<BarEntry>,
xAxisValues: ArrayList<String>,
pickedCurrs: MutableList<String>
) {
val colorsList = mutableListOf<Int>()
val legEn = mutableListOf<LegendEntry>()
for (i in pickedCurrs.indices) {
val color =
Color.argb(
255,
Random().nextInt(256),
Random().nextInt(256),
Random().nextInt(256)
)
if (!colorsList.contains(color)) {
colorsList.add(color)
}
}
set = BarDataSet(myEntries, "x")
set?.colors = colorsList
set?.highLightAlpha = 0
val data = BarData(set)
mBinding.timeSeriesChart.data = data
mBinding.timeSeriesChart.description?.isEnabled = false
mBinding.timeSeriesChart.setVisibleXRangeMaximum(6f)
mBinding.timeSeriesChart.barData?.setValueTextSize(12f)
mBinding.timeSeriesChart.xAxis?.position = XAxis.XAxisPosition.BOTTOM
mBinding.timeSeriesChart.setExtraOffsets(0f, 0f, 0f, 15f)
//
// //xAxis
mBinding.timeSeriesChart.xAxis?.setDrawLabels(true)
mBinding.timeSeriesChart.xAxis?.position = XAxis.XAxisPosition.BOTTOM_INSIDE
mBinding.timeSeriesChart.xAxis?.granularity = 1f
mBinding.timeSeriesChart.xAxis?.textSize = 11.5f
mBinding.timeSeriesChart.xAxis?.valueFormatter = IndexAxisValueFormatter(myXAxisValues)
mBinding.timeSeriesChart.xAxis?.labelRotationAngle = -20f
mBinding.timeSeriesChart.axisRight?.setDrawGridLines(false)
mBinding.timeSeriesChart.axisLeft.isEnabled = false
mBinding.timeSeriesChart.axisRight.isEnabled = false
//
// //legend
val legend = mBinding.timeSeriesChart.legend
legend?.verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM
legend?.horizontalAlignment = Legend.LegendHorizontalAlignment.LEFT
legend?.orientation = Legend.LegendOrientation.HORIZONTAL
legend?.setDrawInside(false)
for (i in pickedCurrs.indices) {
legEn.add(
LegendEntry(
pickedCurrs[i],
Legend.LegendForm.SQUARE,
15f,
15f,
null,
colorsList[i]
)
)
}
legend?.setCustom(legEn)
set?.valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return String.format("%2.02f", value)
}
}
mBinding.timeSeriesChart.invalidate()
mBinding.timeSeriesChart.refreshDrawableState()
}
编辑堆栈跟踪
Process: com.example.x, PID: 15091
java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
at jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.util.Objects.checkIndex(Objects.java:359)
at java.util.ArrayList.get(ArrayList.java:434)
at com.github.mikephil.charting.renderer.LegendRenderer.renderLegend(LegendRenderer.java:377)
at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:281)
at android.view.View.draw(View.java:23889)
at android.view.View.updateDisplayListIfDirty(View.java:22756)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1994)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.draw(View.java:23892)
at android.view.View.updateDisplayListIfDirty(View.java:22756)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw(ConstraintLayout.java:1994)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at androidx.recyclerview.widget.RecyclerView.drawChild(RecyclerView.java:5545)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.draw(View.java:23892)
at androidx.recyclerview.widget.RecyclerView.draw(RecyclerView.java:4944)
at android.view.View.updateDisplayListIfDirty(View.java:22756)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at androidx.fragment.app.FragmentContainerView.drawChild(FragmentContainerView.kt:235)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at androidx.fragment.app.FragmentContainerView.dispatchDraw(FragmentContainerView.kt:225)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at androidx.fragment.app.FragmentContainerView.drawChild(FragmentContainerView.kt:235)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at androidx.fragment.app.FragmentContainerView.dispatchDraw(FragmentContainerView.kt:225)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
at android.view.View.updateDisplayListIfDirty(View.java:22747)
at android.view.View.draw(View.java:23620)
at android.view.ViewGroup.drawChild(ViewGroup.java:4556)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4317)
1条答案
按热度按时间hwamh0ep1#
TL;DR调用
legend.setCustom(entries)
后再调用chart.notifyDataSetChanged()
我认为这是MPDandroidChart中的一个bug。如果您增加自定义图例中的条目数并通知图表数据集已更改,则会发生崩溃。我做了一个简单的例子,复制这个问题来测试修复。
如果您运行这个演示问题并单击“添加条目”按钮,它将崩溃,并出现您看到的相同错误。
原因是Legend对象(
calculatedLabelSizes
)中计算的图例文本宽度数组在渲染图例之前不会自动更新为新增加的条目数,从而导致LegendRenderer中的IndexOutOfBoundsException。修复方法是在图表上调用notifyDataSetChanged
,以强制它重新测量图例,并在渲染之前调整内部数组的大小以匹配图例条目的数量,如下所示这样做,演示工作正常,新的图例条目可以添加没有问题。
注意
您的
if (!colorsList.contains(color)) {
子句也有可能导致您的应用崩溃。如果您的随机颜色生成器遇到重复的颜色,colorsList
长度将太短。这不太可能,但并非不可能。