项目需要用到数据曲线,而且曲线是实时变化的,所以选择了第三方图表库Charts。选择Charts的最主要原因还是它是作者按照Android的常用曲线库MPAndroidChart写的,功能UI基本相近。
曲线的基本用法很简单,在GitHub上可以下载到demo,有各种示例,K线图、折线图和饼状图等都有。以下主要讲我自己碰到的特定问题和解决方案。
项目曲线是根据时间不停变化的,而坐标轴是一个点一个数据,也就是对应的一秒一个数据。但是项目需求坐标单位是分钟,所以每个坐标间就有60个数据。
ChartXAxis有两个属性:axisMaximum、labelCount
/// The maximum value for this axis.
/// If set, this value will not be calculated automatically depending on the provided data.
/// Use `resetCustomAxisMax()` to undo this.
/// the number of label entries the axis should have
/// max = 25,
/// min = 2,
/// default = 6,
/// be aware that this number is not fixed and can only be approximated
按照注释的意思,我只需要设置一个最大数值,然后再设置坐标显示个数,比如我设置
xAxis.axisMaximum = 600;
[xAxis setLabelCount:11];
这样就相当于坐标轴上显示从0到10的坐标,单位是分钟,一共有600秒的数据。然而曲线上实际只显示了6个数字,中间只有5个间隔。我百思不得其解,只能从源码入手。
打开AxisRendererBase.swift文件,找到computeAxisValues函数,代码如下:
@objc open func computeAxisValues(min: Double, max: Double)
{
guard let axis = self.axis else { return }
let yMin = min
let yMax = max
let labelCount = axis.labelCount
let range = abs(yMax - yMin)
if labelCount == 0 || range <= 0 || range.isInfinite
{
axis.entries = [Double]()
axis.centeredEntries = [Double]()
return
}
// Find out how much spacing (in y value space) between axis values
let rawInterval = range / Double(labelCount)
var interval = rawInterval.roundedToNextSignficant()
// If granularity is enabled, then do not allow the interval to go below specified granularity.
// This is used to avoid repeated values when rounding values for display.
if axis.granularityEnabled
{
interval = interval < axis.granularity ? axis.granularity : interval
}
// Normalize interval
let intervalMagnitude = pow(10.0, Double(Int(log10(interval)))).roundedToNextSignficant()
let intervalSigDigit = Int(interval / intervalMagnitude)
if intervalSigDigit > 5
{
// Use one order of magnitude higher, to avoid intervals like 0.9 or 90
interval = floor(10.0 * Double(intervalMagnitude))
}
var n = axis.centerAxisLabelsEnabled ? 1 : 0
// force label count
if axis.isForceLabelsEnabled
{
interval = Double(range) / Double(labelCount - 1)
// Ensure stops contains at least n elements.
axis.entries.removeAll(keepingCapacity: true)
axis.entries.reserveCapacity(labelCount)
var v = yMin
for _ in 0 ..< labelCount
{
axis.entries.append(v)
v += interval
}
n = labelCount
}
else
{
// no forced count
var first = interval == 0.0 ? 0.0 : ceil(yMin / interval) * interval
if axis.centerAxisLabelsEnabled
{
first -= interval
}
let last = interval == 0.0 ? 0.0 : (floor(yMax / interval) * interval).nextUp
if interval != 0.0 && last != first
{
for _ in stride(from: first, through: last, by: interval)
{
n += 1
}
}
else if last == first && n == 0
{
n = 1
}
// Ensure stops contains at least n elements.
axis.entries.removeAll(keepingCapacity: true)
axis.entries.reserveCapacity(labelCount)
var f = first
var i = 0
while i < n
{
if f == 0.0
{
// Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0)
f = 0.0
}
axis.entries.append(Double(f))
f += interval
i += 1
}
}
// set decimals
if interval < 1
{
axis.decimals = Int(ceil(-log10(interval)))
}
else
{
axis.decimals = 0
}
if axis.centerAxisLabelsEnabled
{
axis.centeredEntries.reserveCapacity(n)
axis.centeredEntries.removeAll()
let offset: Double = interval / 2.0
for i in 0 ..< n
{
axis.centeredEntries.append(axis.entries[i] + offset)
}
}
}
可以看到这几句代码
let intervalMagnitude = pow(10.0, Double(Int(log10(interval)))).roundedToNextSignficant()
let intervalSigDigit = Int(interval / intervalMagnitude)
if intervalSigDigit > 5
{
// Use one order of magnitude higher, to avoid intervals like 0.9 or 90
interval = floor(10.0 * Double(intervalMagnitude))
}
这下找到罪魁祸首了,原来在这里对labelCount还做了处理,找到了原因解决起来就不难了,只有重写这个函数,将labelCount保持设置的值,就可以显示正常的x坐标轴了。