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

如何让JPanel的paintComponent仅重绘正在编辑的Landmass对象?

解决方案

1. 静态内容缓存+动态层绘制

这是解决重绘性能问题最直接有效的方案,核心思路是把无需实时编辑的静态内容提前渲染到缓存图中,每次重绘只加载缓存图,再单独绘制当前正在编辑的对象:

步骤1:添加缓存变量

在你的Workspace类中新增一个BufferedImage作为静态地图缓存:

private BufferedImage staticMapCache;

步骤2:实现缓存更新方法

编写专门的缓存更新逻辑,仅当静态内容发生变化(比如新增/删除大陆、修改非编辑状态的大陆属性)时调用:

private void updateStaticCache() {
    // 缓存尺寸与面板不一致时重新创建
    if (staticMapCache == null || staticMapCache.getWidth() != getWidth() || staticMapCache.getHeight() != getHeight()) {
        staticMapCache = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
    }
    
    Graphics2D g2 = staticMapCache.createGraphics();
    // 复用paintComponent中的初始化逻辑(缩放、半径设置)
    zoom(g2);
    int radius = zoomHandler.divZ(this.radius);
    graphicsHandler.setRadius(radius);
    
    // 只绘制非选中(非编辑状态)的大陆
    for (int i = 0; i < landmasses.size(); i++) {
        Landmass lm = landmasses.get(i);
        if (!lm.getIsSelected()) {
            graphicsHandler.drawLandmass(g2, lm, i);
        }
    }
    g2.dispose();
}

步骤3:修改paintComponent方法

重绘时先渲染静态缓存,再单独绘制当前编辑的大陆:

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    
    // 绘制静态缓存图
    if (staticMapCache != null) {
        g2.drawImage(staticMapCache, 0, 0, this);
    }
    
    // 绘制当前选中(编辑中)的大陆
    zoom(g2);
    int radius = zoomHandler.divZ(this.radius);
    graphicsHandler.setRadius(radius);
    
    for (int i = 0; i < landmasses.size(); i++) {
        Landmass lm = landmasses.get(i);
        if (lm.getIsSelected()) {
            graphicsHandler.drawLandmass(g2, lm, i);
        }
    }
}

步骤4:触发缓存更新的时机

  • 新增/删除Landmass对象时调用updateStaticCache()
  • 修改非编辑状态的Landmass属性(如颜色、节点坐标)时调用updateStaticCache()
  • 窗口大小变化或缩放比例调整时调用updateStaticCache()
  • 选中/取消选中大陆时:选中前调用updateStaticCache()(将该大陆从缓存中移除),取消选中后调用updateStaticCache()(将该大陆重新加入缓存)

2. 结合区域重绘进一步优化

为了缩小重绘范围,使用repaint(Rectangle)重载方法,仅重绘当前编辑大陆的边界区域:

步骤1:给Landmass添加边界获取方法

利用已有的prox数组,返回大陆的边界矩形(需考虑缩放和节点半径):

public Rectangle getBounds() {
    int radius = zoomHandler.divZ(this.radius); // 使用当前缩放后的半径
    return new Rectangle(prox[0] - radius, prox[1] - radius,
            prox[2] - prox[0] + 2 * radius,
            prox[3] - prox[1] + 2 * radius);
}

步骤2:修改重绘触发逻辑

鼠标移动编辑大陆时,替换全局repaint()为区域重绘:

Landmass editingLandmass = ...; // 当前正在编辑的大陆对象
repaint(editingLandmass.getBounds());

这样Swing只会重绘指定的矩形区域,而非整个面板,进一步降低性能消耗。

注意事项

  • 缓存更新不要过于频繁,仅在静态内容确实发生变化时触发
  • 缩放比例改变时必须更新缓存,因为所有大陆的位置和尺寸都会随之变化
  • 若编辑操作会覆盖其他大陆,需确保缓存能正确处理图层重叠问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 16:17:01