绘制K线第一章:可见区间处理
概述
在入门版本中,我们绘制了所有K线数据。但当数据量很大时,所有K线会挤在一起或超出屏幕,无法正常显示。
可见区间处理就是解决这个问题:只绘制屏幕能容纳的K线数量,让图表清晰可读。
为什么需要可见区间
问题场景
假设有以下情况:
- 屏幕宽度:1080px
- 每根K线占用:30px(20px宽度 + 10px间距)
- 可显示的K线数量:1080 ÷ 30 = 36根
- 实际数据:1000根K线
如果绘制所有1000根K线:
- K线会挤在一起,无法看清
- 或者超出屏幕,看不到完整内容
解决方案
只显示最后36根K线(最新的数据),这样:
- K线清晰可读
- 不会超出屏幕
实现原理
核心逻辑
scss
// 1. 计算可见K线数量
val totalCandleWidth = config.getTotalCandleWidth() // 30px
val visibleCount = (width / totalCandleWidth).toInt() // 1080 ÷ 30 = 36
// 2. 获取可见数据(只取最后N根)
val visibleData = if (klineData.size > visibleCount) {
klineData.takeLast(visibleCount) // 取最后36根
} else {
klineData // 数据不足,全部显示
}
// 3. 只对可见数据计算价格范围
val priceRange = calculatePriceRange(visibleData)
// 4. 只绘制可见数据
visibleData.forEachIndexed { index, entity ->
drawCandle(...)
}
关键点
- 计算可见数量 :
屏幕宽度 ÷ 每根K线占用宽度 - 取最后N根 :使用
takeLast()方法,显示最新的数据 - 价格范围计算:只基于可见数据计算,确保Y轴比例正确
代码实现(KLineViewCase2)
类图
kotlin
@startuml
class KLineViewCase2 {
- klineData: List<KLineEntity>
- config: KLineConfig
+ setKLineData(data: List<KLineEntity>)
- calculatePriceRange(data: List<KLineEntity>): Pair<Float, Float>
}
KLineViewCase2 --> KLineConfig : 依赖
KLineViewCase2 --> KLineEntity : 使用
note right of KLineViewCase2
相比Case1,增加了可见区间处理:
1. 计算可见K线数量
2. 只取最后N根数据
3. 只对可见数据计算价格范围
end note
@enduml
核心代码(可见区间部分)
在 onDraw() 方法中,相比Case1增加了可见区间处理:
scss
// 1. 计算可见区间:根据屏幕宽度计算能显示多少根K线
val totalCandleWidth = config.getTotalCandleWidth()
val visibleCount = (width / totalCandleWidth).toInt()
// 2. 获取可见的K线数据(只显示最后N根,最新的数据)
val visibleData = if (klineData.size > visibleCount) {
klineData.takeLast(visibleCount) // 取最后N根
} else {
klineData // 数据不足,全部显示
}
// 3. 计算可见数据的价格范围(注意:传入visibleData而不是klineData)
val priceRange = calculatePriceRange(visibleData)
// 4. 绘制可见的K线(使用visibleData)
visibleData.forEachIndexed { index, entity ->
drawCandle(canvas, entity, index, minPrice, maxPrice, height)
}
关键改动:
calculatePriceRange()方法现在接受data参数,可以传入可见数据- 其他绘制逻辑与Case1相同(参考入门文档)
与Case1的对比
| 特性 | Case1(入门版) | Case2(可见区间版) |
|---|---|---|
| 绘制数据 | 所有数据 | 只绘制可见数据(最后N根) |
| 价格范围计算 | 基于所有数据 | 基于可见数据 |
| 适用场景 | 数据量小(< 50根) | 数据量大(> 50根) |
| 代码复杂度 | 简单 | 稍复杂(增加可见区间逻辑) |
完整绘制链路
相比Case1,Case2在绘制链路中增加了可见区间处理:
scss
1. 设置K线数据 → 触发onDraw()重绘
↓
2. onDraw()方法执行:
├─ 绘制背景(与Case1相同)
├─ 检查数据是否为空(与Case1相同)
├─ 【新增】计算可见区间
│ ├─ 计算可见K线数量(屏幕宽度 ÷ 每根K线宽度)
│ └─ 获取可见数据(取最后N根)
├─ 计算价格范围(基于可见数据,不是所有数据)
└─ 遍历可见K线数据并绘制(绘制逻辑与Case1相同)
说明:绘制K线的具体步骤(计算坐标、绘制影线、绘制实体)与Case1相同,参考入门文档。
关键概念
可见K线数量计算
scss
可显示的K线数量 = 屏幕宽度 ÷ 每根K线占用的总宽度
= width ÷ (candleWidth + candleSpacing)
= width ÷ totalCandleWidth
示例:
- 屏幕宽度:1080px
- 每根K线占用:30px(20px宽度 + 10px间距)
- 可见数量:1080 ÷ 30 = 36根
取最后N根数据
arduino
val visibleData = if (klineData.size > visibleCount) {
klineData.takeLast(visibleCount) // 取最后N根(最新数据)
} else {
klineData // 数据不足,全部显示
}
说明:
- 如果数据量 > 可见数量:只取最后N根(显示最新数据)
- 如果数据量 ≤ 可见数量:全部显示
价格范围只基于可见数据
这是关键点:价格范围必须只基于可见数据计算,而不是所有数据。
为什么?
- 如果基于所有数据计算价格范围,可见数据的价格变化可能很小
- 导致Y轴比例不合适,K线看起来像一条线
- 只基于可见数据计算,Y轴比例正确,K线清晰可见
注意事项
- 数据顺序 :确保数据是按时间顺序排列的(从旧到新),这样
takeLast()才能取到最新数据 - 边界情况:当数据量小于可见数量时,需要全部显示
- 价格范围:必须只基于可见数据计算,不能基于所有数据
总结
可见区间处理是K线绘制的重要优化:
- 解决数据量大时的显示问题
- 只绘制屏幕能容纳的K线
- 价格范围只基于可见数据计算
- 为后续的滚动、缩放功能打下基础
效果
