Undecorated JFrame西北边缘拖动仅改位置不改大小问题求助
问题:无装饰JFrame北/西边缘拖动仅移动窗口不调整大小
我在做一个训练项目,创建了无装饰(Undecorated)的JFrame,通过添加MouseMotionListener实现光标样式切换,模拟带装饰窗口的大小调整和位置移动功能。但遇到一个问题:拖动窗口北边缘或西边缘时,窗口只会移动位置,不会调整大小,但调试模式下功能正常。
我试过用线程、替换setSize为setBounds,还调用了frame.revalidate()和frame.repaint(),都没解决问题,求建议。
问题根源
你的代码存在两个核心逻辑错误:
- 基准位置记录时机错误:
tempX/tempY在mouseMoved中实时更新,但拖动时需要的是鼠标按下瞬间的初始位置,而非移动过程中的位置,这导致位置计算完全偏离预期。 - 西/北边缘的尺寸-位置联动逻辑错误:拖动这两个边缘时,需要同时调整窗口的尺寸和位置,但当前的距离计算、位置更新公式都不符合实际需求,且
lastSize没有在拖动开始时锁定初始状态。
另外,给JPanel设置固定的min/preferred/maxSize会强制限制窗口尺寸,直接阻碍了大小调整功能。
修复后的完整代码
package newpackage; import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseAdapter; public class ResizeUndecoratedJFrame { private int dragStartX; private int dragStartY; private Dimension dragStartSize; private Point dragStartLocation; private Dimension minFrameSize; public ResizeUndecoratedJFrame() { JFrame frame = new JFrame(); frame.setUndecorated(true); JPanel p = new JPanel(); // 移除面板的固定尺寸限制,让其随窗口自适应 minFrameSize = new Dimension(400, 300); frame.add(p); frame.setSize(400, 300); frame.setLocationRelativeTo(null); // 用MouseAdapter统一处理所有鼠标事件 frame.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { // 锁定拖动开始时的基准数据:屏幕鼠标位置、窗口尺寸、窗口位置 dragStartX = e.getXOnScreen(); dragStartY = e.getYOnScreen(); dragStartSize = frame.getSize(); dragStartLocation = frame.getLocation(); } }); frame.addMouseMotionListener(new MouseAdapter() { @Override public void mouseMoved(MouseEvent e) { int w = frame.getWidth(); int h = frame.getHeight(); int x = e.getX(); int y = e.getY(); // 根据鼠标位置切换对应光标(含边角拖动支持) if (x <= 3 && y <= 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); } else if (x <= 3 && y >= h - 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)); } else if (x >= w - 3 && y <= 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); } else if (x >= w - 3 && y >= h - 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); } else if (x <= 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); } else if (x >= w - 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); } else if (y <= 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); } else if (y >= h - 3) { frame.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); } else { frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } @Override public void mouseDragged(MouseEvent e) { Cursor cursor = frame.getCursor(); int cursorType = cursor.getType(); int currentX = e.getXOnScreen(); int currentY = e.getYOnScreen(); // 计算鼠标拖动的偏移量 int deltaX = currentX - dragStartX; int deltaY = currentY - dragStartY; // 初始化新的窗口尺寸和位置 int newWidth = dragStartSize.width; int newHeight = dragStartSize.height; int newX = dragStartLocation.x; int newY = dragStartLocation.y; // 根据光标类型处理不同的调整逻辑 switch (cursorType) { case Cursor.E_RESIZE_CURSOR: newWidth = dragStartSize.width + deltaX; break; case Cursor.W_RESIZE_CURSOR: newWidth = dragStartSize.width - deltaX; newX = dragStartLocation.x + deltaX; break; case Cursor.S_RESIZE_CURSOR: newHeight = dragStartSize.height + deltaY; break; case Cursor.N_RESIZE_CURSOR: newHeight = dragStartSize.height - deltaY; newY = dragStartLocation.y + deltaY; break; case Cursor.SE_RESIZE_CURSOR: newWidth = dragStartSize.width + deltaX; newHeight = dragStartSize.height + deltaY; break; case Cursor.NE_RESIZE_CURSOR: newWidth = dragStartSize.width + deltaX; newHeight = dragStartSize.height - deltaY; newY = dragStartLocation.y + deltaY; break; case Cursor.SW_RESIZE_CURSOR: newWidth = dragStartSize.width - deltaX; newX = dragStartLocation.x + deltaX; newHeight = dragStartSize.height + deltaY; break; case Cursor.NW_RESIZE_CURSOR: newWidth = dragStartSize.width - deltaX; newX = dragStartLocation.x + deltaX; newHeight = dragStartSize.height - deltaY; newY = dragStartLocation.y + deltaY; break; default: // 默认处理窗口整体拖动 newX = dragStartLocation.x + deltaX; newY = dragStartLocation.y + deltaY; break; } // 限制窗口最小尺寸,同时修正边缘拖动时的位置偏移 if (newWidth < minFrameSize.width) { newWidth = minFrameSize.width; if (cursorType == Cursor.W_RESIZE_CURSOR || cursorType == Cursor.NW_RESIZE_CURSOR || cursorType == Cursor.SW_RESIZE_CURSOR) { newX = dragStartLocation.x + (dragStartSize.width - newWidth); } } if (newHeight < minFrameSize.height) { newHeight = minFrameSize.height; if (cursorType == Cursor.N_RESIZE_CURSOR || cursorType == Cursor.NW_RESIZE_CURSOR || cursorType == Cursor.NE_RESIZE_CURSOR) { newY = dragStartLocation.y + (dragStartSize.height - newHeight); } } // 统一更新窗口位置和尺寸 frame.setBounds(newX, newY, newWidth, newHeight); } }); frame.setVisible(true); } public static void main(String[] args) { // 按Swing线程安全规范启动UI SwingUtilities.invokeLater(ResizeUndecoratedJFrame::new); } }
关键修复点
- 锁定拖动基准数据:在
mousePressed中记录拖动开始时的屏幕鼠标位置、窗口初始尺寸和位置,确保后续计算有稳定的基准。 - 修正西/北边缘逻辑:拖动西边缘时,宽度减少偏移量的同时,窗口X坐标同步增加偏移量;拖动北边缘时同理,保证尺寸和位置联动正确。
- 移除面板固定尺寸:取消JPanel的
min/preferred/maxSize设置,只保留窗口级别的最小尺寸限制。 - 补充边角拖动支持:新增四个边角的光标切换和调整逻辑,功能更贴近原生窗口。
- 线程安全启动UI:使用
SwingUtilities.invokeLater启动界面,符合Swing的线程安全要求。
内容的提问来源于stack exchange,提问作者tans flou
相关产品推荐
相关产品推荐

