Java Swing窗口重绘行为解析及边框式Resize实现咨询
JTable窗口调整大小的重绘问题解析与解决方案
先看你提供的示例代码:
import javax.swing.*; public class JTableSimpleSample { JFrame f; JTableSimpleSample(){ f=new JFrame(); String data[][]={ {"101","Amit","670000"}, {"102","Jai","780000"}, {"101","Sachin","700000"}}; String column[]={"ID","NAME","SALARY"}; JTable jt=new JTable(data,column); jt.setBounds(30,40,200,300); JScrollPane sp=new JScrollPane(jt); f.add(sp); f.setSize(300,400); f.setVisible(true); } public static void main(String[] args) { new JTableSimpleSample(); } }
调整窗口大小时的重绘异常表现
拖动窗口不同边缘时,重绘效果差异明显:
- 顶部边缘拖动:整体相对整洁,但JFrame底部边缘持续重绘,出现多线条残留
- 底部边缘拖动:放大时先显示黑块,拖动越快黑块范围越大,重绘完成后消失
- 右侧边缘拖动:表现与底部边缘类似
- 左侧边缘拖动:问题最严重,右侧边缘出现多线条残留,甚至表格数据会错乱
- 角落拖动:是对应两个边缘拖动效果的组合
问题1:为何不同边缘拖动会出现这些差异?
这主要和Swing的重绘机制以及你代码里的布局方式脱不开关系:
- 手动布局的局限性:你用
setBounds()给JTable固定了位置和大小,没有使用Swing的布局管理器(比如BorderLayout)。当窗口调整大小时,布局管理器会自动计算组件的新位置和大小,但手动设置的组件无法自动适配,导致重绘时新旧区域的衔接出现混乱。 - 重绘区域的逻辑差异:Swing的
RepaintManager只会重绘需要更新的区域:- 拖动顶部/左侧时,窗口的坐标(x或y轴)发生偏移,整个窗口的位置移动,需要重绘的区域是原窗口和新窗口的叠加部分,这会触发更复杂的重绘逻辑,容易出现残留或数据错乱;
- 拖动底部/右侧时,窗口的宽高变化,只需要重绘新增的空白区域,这部分区域默认会填充系统窗口的背景色(通常是黑色),所以拖动时会先看到黑块,等组件重绘完成后才会覆盖。
- 系统窗口管理器的影响:不同操作系统的窗口管理器处理窗口调整的调度优先级不同,这也会加剧不同边缘拖动时的表现差异。
问题2:如何实现拖动时仅显示边框轮廓,替换黑块为透明效果?
当然可以实现,这里给你两种实用的方案:
方案1:延迟重绘+半透明效果(解决黑块问题)
通过监听窗口调整事件,在调整过程中延迟重绘,同时设置窗口半透明替代黑块,调整结束后立即刷新:
import javax.swing.*; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.util.Timer; import java.util.TimerTask; public class JTableResizeFixSample { JFrame f; Timer resizeTimer; boolean isResizing = false; JTableResizeFixSample() { f = new JFrame(); String data[][] = {{"101", "Amit", "670000"}, {"102", "Jai", "780000"}, {"101", "Sachin", "700000"}}; String column[] = {"ID", "NAME", "SALARY"}; JTable jt = new JTable(data, column); JScrollPane sp = new JScrollPane(jt); // 用布局管理器替代setBounds,让组件自动适配窗口大小 f.setLayout(new BorderLayout()); f.add(sp, BorderLayout.CENTER); // 添加窗口调整监听 f.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (!isResizing) { // 开始调整时设置半透明 f.setOpacity(0.8f); isResizing = true; } // 重置定时器,避免频繁重绘 if (resizeTimer != null) { resizeTimer.cancel(); } resizeTimer = new Timer(); resizeTimer.schedule(new TimerTask() { @Override public void run() { SwingUtilities.invokeLater(() -> { // 调整结束后恢复不透明并强制重绘 f.setOpacity(1.0f); f.repaint(); isResizing = false; }); } }, 100); // 延迟时间可根据需求调整 } }); f.setSize(300, 400); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } public static void main(String[] args) { new JTableResizeFixSample(); } }
方案2:自定义边框轮廓(拖动时仅显示边框)
如果要实现拖动时只显示窗口边框、隐藏内容的效果,可以通过无装饰窗口+自定义绘制实现:
import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Rectangle2D; public class ResizeOutlineSample { JFrame f; boolean isResizing = false; Point startPoint; Dimension startSize; ResizeOutlineSample() { f = new JFrame(); f.setUndecorated(true); // 去掉默认窗口边框 f.setBackground(new Color(0, 0, 0, 0)); // 设置窗口透明 // 添加内容面板 JPanel contentPane = new JPanel(new BorderLayout()); contentPane.setBackground(Color.WHITE); String data[][] = {{"101", "Amit", "670000"}, {"102", "Jai", "780000"}, {"101", "Sachin", "700000"}}; String column[] = {"ID", "NAME", "SALARY"}; JTable jt = new JTable(data, column); contentPane.add(new JScrollPane(jt), BorderLayout.CENTER); f.add(contentPane); // 自定义窗口边框绘制(通过GlassPane实现) f.setGlassPane(new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (isResizing) { // 绘制黑色边框轮廓 Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.BLACK); g2d.setStroke(new BasicStroke(2)); g2d.draw(new Rectangle2D.Double(0, 0, f.getWidth() - 1, f.getHeight() - 1)); } } }); f.getGlassPane().setVisible(true); // 添加鼠标监听处理拖动逻辑 f.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { startPoint = e.getLocationOnScreen(); startSize = f.getSize(); isResizing = true; contentPane.setVisible(false); // 拖动时隐藏内容 } @Override public void mouseReleased(MouseEvent e) { isResizing = false; contentPane.setVisible(true); // 拖动结束后恢复内容 f.repaint(); } }); f.addMouseMotionListener(new MouseAdapter() { @Override public void mouseDragged(MouseEvent e) { if (isResizing) { Point currentPoint = e.getLocationOnScreen(); int newWidth = startSize.width + (currentPoint.x - startPoint.x); int newHeight = startSize.height + (currentPoint.y - startPoint.y); // 限制最小窗口大小 f.setSize(Math.max(200, newWidth), Math.max(200, newHeight)); f.getGlassPane().repaint(); } } }); f.setSize(300, 400); f.setLocationRelativeTo(null); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(ResizeOutlineSample::new); } }
内容的提问来源于stack exchange,提问作者Unhandled Exception
相关产品推荐
相关产品推荐

