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

Undecorated JFrame西北边缘拖动仅改位置不改大小问题求助

问题:无装饰JFrame北/西边缘拖动仅移动窗口不调整大小

我在做一个训练项目,创建了无装饰(Undecorated)的JFrame,通过添加MouseMotionListener实现光标样式切换,模拟带装饰窗口的大小调整和位置移动功能。但遇到一个问题:拖动窗口北边缘或西边缘时,窗口只会移动位置,不会调整大小,但调试模式下功能正常。

我试过用线程、替换setSize为setBounds,还调用了frame.revalidate()和frame.repaint(),都没解决问题,求建议。


问题根源

你的代码存在两个核心逻辑错误:

  1. 基准位置记录时机错误:tempX/tempY在mouseMoved中实时更新,但拖动时需要的是鼠标按下瞬间的初始位置,而非移动过程中的位置,这导致位置计算完全偏离预期。
  2. 西/北边缘的尺寸-位置联动逻辑错误:拖动这两个边缘时,需要同时调整窗口的尺寸和位置,但当前的距离计算、位置更新公式都不符合实际需求,且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);
    }
}

关键修复点

  1. 锁定拖动基准数据:在mousePressed中记录拖动开始时的屏幕鼠标位置、窗口初始尺寸和位置,确保后续计算有稳定的基准。
  2. 修正西/北边缘逻辑:拖动西边缘时,宽度减少偏移量的同时,窗口X坐标同步增加偏移量;拖动北边缘时同理,保证尺寸和位置联动正确。
  3. 移除面板固定尺寸:取消JPanel的min/preferred/maxSize设置,只保留窗口级别的最小尺寸限制。
  4. 补充边角拖动支持:新增四个边角的光标切换和调整逻辑,功能更贴近原生窗口。
  5. 线程安全启动UI:使用SwingUtilities.invokeLater启动界面,符合Swing的线程安全要求。

内容的提问来源于stack exchange,提问作者tans flou

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 14:47:04