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

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的重绘机制以及你代码里的布局方式脱不开关系:

  1. 手动布局的局限性:你用setBounds()给JTable固定了位置和大小,没有使用Swing的布局管理器(比如BorderLayout)。当窗口调整大小时,布局管理器会自动计算组件的新位置和大小,但手动设置的组件无法自动适配,导致重绘时新旧区域的衔接出现混乱。
  2. 重绘区域的逻辑差异:Swing的RepaintManager只会重绘需要更新的区域:
    • 拖动顶部/左侧时,窗口的坐标(x或y轴)发生偏移,整个窗口的位置移动,需要重绘的区域是原窗口和新窗口的叠加部分,这会触发更复杂的重绘逻辑,容易出现残留或数据错乱;
    • 拖动底部/右侧时,窗口的宽高变化,只需要重绘新增的空白区域,这部分区域默认会填充系统窗口的背景色(通常是黑色),所以拖动时会先看到黑块,等组件重绘完成后才会覆盖。
  3. 系统窗口管理器的影响:不同操作系统的窗口管理器处理窗口调整的调度优先级不同,这也会加剧不同边缘拖动时的表现差异。

问题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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 21:12:49