You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中使用SWT在Canvas绘制XY图表 解决刻度消失、末端不绘制问题

问题根因
  • 整数除法精度丢失:你使用int类型计算step_x,当列表长度大于等于画布宽度时,canvas.getClientArea().width / list.size()的整数除法结果直接为0,循环判断条件step_x > 0不满足,直接终止绘制逻辑,刻度自然全部消失。
  • 刻度与数据点强绑定逻辑不合理:当前逻辑每一个数据点对应一个待绘制刻度,数据量上涨后相邻刻度的间距小于文本宽度,重叠校验会把所有后续刻度都跳过,最终只剩前几个甚至完全没有刻度。
  • 并发读写冲突:数据添加操作在并行线程执行,绘制线程读取列表、计算step_x的过程中可能出现列表长度不一致的问题,要么计算的step_x和实际遍历的数据量不匹配,要么遍历列表时抛出并发修改异常,最终导致末端数据绘制异常。
  • 循环逻辑缺陷:step_x变小后,循环累加i的步长太小,当数据量极大时i的累加速度跟不上列表遍历速度,会出现列表遍历完了i还没到画布末端,或者i溢出变成负数的情况,导致末端数据没有正常绘制。
修复方案

1. 优化刻度生成逻辑,解除与数据点的绑定

固定横向显示的刻度上限,比如最多显示8~10个时间刻度,不管数据量多少,只按均匀间隔取刻度点,避免数据量上涨后刻度挤在一起:

// 固定最多显示10个刻度,可根据画布宽度动态调整
int maxNotchCount = 10;
int actualNotchCount = Math.min(maxNotchCount, drawList.size());
for (int n = 0; n < actualNotchCount; n++) {
    // 按间隔取对应的数据点作为刻度锚点
    int dataIndex = n * drawList.size() / actualNotchCount;
    GraphData data = drawList.get(dataIndex);
    // 计算刻度的x坐标
    int x = client_area.x + (int) (dataIndex * stepX);
    String notchString = returnTime(data);
    int notchWidth = a_gc.stringExtent(notchString).x;
    int curBegX = x - notchWidth / 2;
    if (prevEndX < curBegX) {
        a_gc.drawLine(x, client_area.y, x, client_area.height);
        a_gc.drawString(notchString, curBegX, convertGraphtoLeftBotCorner(client_area.y + NOTCH_LENGTH, a_canvas));
        prevEndX = curBegX + notchWidth;
    }
}

2. 修正步长计算逻辑,避免整数除法为0

用浮点数计算步长,绘制的时候再转成int,同时设置最小步长为1,避免步长为0导致循环终止:

// 并行线程中计算步长,用float类型存储
float stepX = (float) canvas.getClientArea().width / Math.max(1, list.size());

3. 增加并发读写保护

给列表的读写操作加锁,或者每次添加数据后复制一份副本给绘制线程用,避免并发修改问题:

// 并行线程添加数据时加锁
synchronized (list) {
    list.add(newData);
    stepX = (float) canvas.getClientArea().width / list.size();
}
// 绘制线程读取数据前先复制副本,避免绘制过程中列表被修改
List<GraphData> drawList;
synchronized (list) {
    drawList = new ArrayList<>(list);
}
// 后续所有绘制逻辑都使用drawList,不直接操作原列表

4. 优化循环逻辑,确保末端数据绘制

把原来的i累加循环改成按数据索引遍历,计算每个数据点的x坐标,避免步长太小导致i累加不及时:

float stepX = (float) client_area.width / Math.max(1, drawList.size());
for (int idx = 0; idx < drawList.size(); idx++) {
    int x = client_area.x + (int)(idx * stepX);
    // 绘制数据点的逻辑使用该x坐标即可
}

内容的提问来源于stack exchange,提问作者Valdemar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 17:54:04