如何让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
相关产品推荐
相关产品推荐

