paintComponent函数反复执行及boxLabels溢出问题求助
问题排查与修复方案
兄弟,我一眼就看穿你代码里的核心问题了——你把组件创建、添加以及集合修改的逻辑放到了paintComponent方法里,这可是Swing开发的大忌,直接导致了你遇到的两个麻烦:
为什么paintComponent会反复执行?
Swing的paintComponent是专门负责组件绘制的方法,系统会在很多场景下自动触发它:窗口大小变化、鼠标悬停/点击、组件刷新等等。而你在这个方法里执行了this.add(boxLabels.get(i))——往容器里添加组件的操作会触发组件层次结构变更,系统会立刻再次调用paintComponent更新界面,这就形成了无限重绘循环,每次点击标签都会让这个循环变本加厉。
为什么boxLabels列表会持续膨胀?
你每次执行paintComponent时,都会执行boxLabels.add(new JLabel("Contents.."))和boxInfo.add(...),相当于每次重绘都往集合里塞新的标签和信息,完全没有清空或复用逻辑,久而久之集合自然会越来越大,内存占用也会飙升。
具体修复步骤
1. 把组件初始化逻辑移出paintComponent
paintComponent只该负责绘制图形,组件的创建、添加应该放到构造函数或者专门的初始化方法里:
- 在类的成员变量里提前初始化
boxLabels和boxInfo集合 - 当
packedBoxes数据更新时,先清空旧内容再重新创建标签,而不是每次重绘都新建
2. 修正packedBoxes的空判断逻辑
你原来的if (packedBoxes != null || packedBoxes.size() != 0)有bug:如果packedBoxes是null,packedBoxes.size()会直接抛出NullPointerException,应该改成&&:
if (packedBoxes != null && !packedBoxes.isEmpty())
3. 分离绘制逻辑与组件管理
- 绘制箱子和物品的逻辑留在
paintComponent里 - 标签的创建、位置更新、tooltip设置,放到单独的方法中,仅在
packedBoxes数据变化时调用,而不是每次重绘都执行
修复后的代码示例
// 类成员变量 private List<JLabel> boxLabels = new ArrayList<>(); private List<String> boxInfo = new ArrayList<>(); // 初始化或数据更新时调用的方法 public void updateBoxLabels() { // 先清空旧的标签和信息 for (JLabel label : boxLabels) { this.remove(label); } boxLabels.clear(); boxInfo.clear(); if (packedBoxes == null || packedBoxes.isEmpty()) { return; } int drawboxWidth = (getWidth() / packedBoxes.size()) - (100 / packedBoxes.size()) - 5; int drawboxHeight = (getHeight() / 2); for (int i = 0; i < packedBoxes.size(); i++) { JLabel label = new JLabel("Contents.."); int x = 50 + (drawboxWidth * i) + (i * 5); int y = drawboxHeight + 100; label.setBounds(x, y, drawboxWidth, 20); label.setHorizontalAlignment(SwingConstants.CENTER); // 生成tooltip信息 String tooltip = "<html><font face=\"sansserif\"><h2>Box nr."+(i+1)+"</h2>" + "Percentage filled: "+(int)(((float)packedBoxes.get(i).getUsedSize()/(float) boxHeight)*100)+ "% </html>"; label.setToolTipText(tooltip); boxLabels.add(label); this.add(label); } // 触发重绘更新箱子图形 repaint(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); setBackground(Color.LIGHT_GRAY); g.setColor(Color.BLACK); if (packedBoxes != null && !packedBoxes.isEmpty()) { int drawboxWidth = (getWidth() / packedBoxes.size()) - (100 / packedBoxes.size()) - 5; int drawboxHeight = (getHeight() / 2); // 绘制每个箱子里的物品和箱子边框 for (int i = 0; i < packedBoxes.size(); i++) { int x = 50 + (drawboxWidth * i) + (i * 5); int currentY = 50; // 绘制物品 for (Item item : packedBoxes.get(i).getItems()) { float productHeight = (float) item.height / (float) boxHeight; float drawProductHeight = drawboxHeight * productHeight; g.setColor(item.color); g.fillRect(x, currentY, drawboxWidth, (int) drawProductHeight); currentY += drawProductHeight; } // 绘制箱子边框 g.setColor(Color.BLACK); g.drawRect(x, 50, drawboxWidth, drawboxHeight); } } }
额外注意事项
- 当你的
packedBoxes数据更新后,记得调用updateBoxLabels()方法来更新标签,而不是直接触发重绘 - 尽量避免在
paintComponent里执行任何耗时操作或状态修改,保持它只专注于绘制图形 - 可以考虑使用布局管理器(比如
GridLayout或FlowLayout)来管理标签的位置,而不是手动调用setBounds,这样界面适配会更灵活
内容的提问来源于stack exchange,提问作者Remy Van Lis
相关产品推荐
相关产品推荐

