Java Swing 自定义组件库分享(六)

自定义窗口 --- CusFrame

一、背景

Swing 原生窗口 JFrame 的标题栏是系统自带的,样式陈旧且无法自定义。不同操作系统下标题栏风格不一致,也不支持圆角、自定义颜色、自定义按钮等现代 UI 元素。 CusFrame 的作用就是:移除系统装饰,自己绘制标题栏和窗口边框,实现统一的窗口风格。同时支持窗口拖拽移动、调整大小、最小化、最大化、还原、多屏幕适配等功能。

二、类源码

java 复制代码
import lombok.Setter;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.RoundRectangle2D;
import java.util.function.BiConsumer;

/**
 * 自定义窗口框架类
 * 实现圆角窗口和自定义标题栏,支持拖拽移动、调整大小、最小化、最大化和关闭功能
 *  * 使用示例:
 * CusFrame frame = new CusFrame("窗口标题");
 * frame.setSize(800, 600);
 * frame.setVisible(true);
 */
public class CusFrame extends JFrame {
    private static final String ICON_PATH = "icons/logo.png";
    /** 标题栏面板 */
    private JPanel titlePanel;
    /** 鼠标点击初始位置,用于窗口拖拽 */
    private Point initialClick;
    /** 窗口是否最大化标志 */
    @Setter
    private boolean maximized = false;
    /** 窗口是否已最大化 */
    private boolean isMaximized = false;
    private boolean maximizedDisable = false;
    /** 窗口正常状态下的边界 */
    private Rectangle normalBounds;
    /** 图标和标题面板 */
    JPanel iconTitlePanel;
    /** 标题标签 */
    private JLabel titleLabel;
    /** 最小化按钮 */
    private JLabel minButton;
    /** 最大化/还原按钮 */
    private JLabel maxButton;
    /** 关闭按钮 */
    private JLabel closeButton;
    /** 关闭按钮事件 */
    private Runnable closeAction;
    /** 圆角半径 */
    private int cornerRadius = 15;
    /** 是否正在调整窗口大小 */
    private boolean isResizing = false;
    /** 边界检测区域宽度 */
    @Setter
    private int resizeBorder = 10;
    /** 当前调整方向 */
    private int resizeDirection = 0;
    /** 调整方向常量 */
    private static final int NONE = 0;
    private static final int TOP = 1;
    private static final int BOTTOM = 2;
    private static final int LEFT = 4;
    private static final int RIGHT = 8;
    private static final int TOP_LEFT = TOP | LEFT;
    private static final int TOP_RIGHT = TOP | RIGHT;
    private static final int BOTTOM_LEFT = BOTTOM | LEFT;
    private static final int BOTTOM_RIGHT = BOTTOM | RIGHT;
    /** 当前窗口所在的屏幕设备 */
    private GraphicsDevice currentScreenDevice;
    /** 父窗口引用,用于新窗口跟随 */
    @Setter
    private CusFrame parentFrame;
    /** 记录窗口是否已经初始化位置 */
    private boolean locationInitialized = false;
    /** 记录窗口的原始位置 */
    private Point originalLocation;
    /** 记录窗口的原始大小 */
    private Dimension originalSize;

    /**
     * 默认构造函数
     */
    public CusFrame() throws HeadlessException {
        this(null, null);
    }

    /**
     * 带标题的构造函数
     * @param title 窗口标题
     */
    public CusFrame(String title) throws HeadlessException {
        this(title, null);
    }

    /**
     * 带标题和父窗口的构造函数
     * @param title 窗口标题
     * @param parentFrame 父窗口
     */
    public CusFrame(String title, CusFrame parentFrame) throws HeadlessException {
        this.parentFrame = parentFrame;
        // 移除默认标题栏
        setUndecorated(true);
        // 设置窗口初始大小
        setSize(new Dimension(800, 600));
        // 创建自定义标题栏
        createCustomTitleBar(title);
        // 设置内容区域
        setupContentArea();
        // 设置圆角
        setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), cornerRadius, cornerRadius));
        // 设置默认关闭方式
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 注:这里获取图片 需自行实现
        setIconImage(xxxxxx);
        // 保存正常状态下的边界
        normalBounds = getBounds();
        // 初始化当前屏幕设备
        updateCurrentScreenDevice();
        // 添加窗口大小变化监听器,更新圆角形状
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), cornerRadius, cornerRadius));
            }
            @Override
            public void componentMoved(ComponentEvent e) {
                updateCurrentScreenDevice();
            }
            @Override
            public void componentShown(ComponentEvent e) {
                if (maximized) {
                    SwingUtilities.invokeLater(() -> {
                        toggleMaximize();
                        maximized = false;
                    });
                }
            }
        });
    }

    /**
     * 更新当前窗口所在的屏幕设备
     */
    private void updateCurrentScreenDevice() {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] screens = ge.getScreenDevices();
        Rectangle windowBounds = getBounds();
        int centerX = windowBounds.x + windowBounds.width / 2;
        int centerY = windowBounds.y + windowBounds.height / 2;
        for (GraphicsDevice screen : screens) {
            Rectangle screenBounds = screen.getDefaultConfiguration().getBounds();
            if (screenBounds.contains(centerX, centerY)) {
                currentScreenDevice = screen;
                break;
            }
        }
        if (currentScreenDevice == null) {
            currentScreenDevice = ge.getDefaultScreenDevice();
        }
    }

    /**
     * 获取当前窗口所在的屏幕设备
     * @return 当前屏幕设备
     */
    public GraphicsDevice getCurrentScreenDevice() {
        if (currentScreenDevice == null) {
            updateCurrentScreenDevice();
        }
        return currentScreenDevice;
    }

    /**
     * 创建自定义标题栏
     * @param title 标题文本
     */
    private void createCustomTitleBar(String title) {
        // 创建标题栏主面板
        titlePanel = new JPanel(new BorderLayout());
        // 左侧:图标和标题
        iconTitlePanel = new JPanel(new BorderLayout());
        iconTitlePanel.setOpaque(false);
        iconTitlePanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
        // 初始化标题标签
        titleLabel = new JLabel(title);
        titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 16));
        titleLabel.setForeground(Color.BLACK);
        titleLabel.setBorder(BorderFactory.createEmptyBorder(8, 5, 5, 5));
        // 使用包装面板确保垂直居中
        JPanel titleWrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
        titleWrapper.setOpaque(false);
        titleWrapper.add(titleLabel);
        iconTitlePanel.add(titleWrapper, BorderLayout.CENTER);
        // 右侧:窗口控制按钮
        JPanel buttonPanel = new JPanel(new BorderLayout());
        buttonPanel.setOpaque(false);
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
        JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
        rightPanel.setOpaque(false);
        // 最小化按钮,图片只是实例
        minButton = createWindowBtn("icons/window/minimize.png", () -> setState(JFrame.ICONIFIED));
        // 最大化/还原按钮
        maxButton = createWindowBtn("icons/window/maximize.png", this::toggleMaximize);
        // 关闭按钮
        closeButton = createWindowBtn("icons/window/close.png", () -> {
            if (null != closeAction) {
                closeAction.run();
            } else {
                dispose();
            }
        });
        rightPanel.add(minButton);
        rightPanel.add(maxButton);
        rightPanel.add(closeButton);
        // 使用包装面板确保按钮垂直居中
        JPanel buttonWrapper = new JPanel(new BorderLayout());
        buttonWrapper.setOpaque(false);
        buttonWrapper.add(rightPanel, BorderLayout.CENTER);
        buttonPanel.add(buttonWrapper, BorderLayout.CENTER);
        // 添加拖拽功能
        addDragFunctionality(titlePanel);
        // 将左右面板添加到标题栏
        titlePanel.add(iconTitlePanel, BorderLayout.WEST);
        titlePanel.add(buttonPanel, BorderLayout.EAST);
        // 将标题栏添加到窗口顶部
        getContentPane().add(titlePanel, BorderLayout.NORTH);
    }

    /**
     * 关闭按钮监听事件
     * @param closeAction 关闭事件
     */
    public void addCloseAction(Runnable closeAction) {
        this.closeAction = closeAction;
    }

    /**
     * 关闭按钮监听事件(带图标)
     * @param iconPath 图标路径
     * @param closeAction 关闭事件
     */
    public void addCloseAction(String iconPath, Runnable closeAction) {
        // 注:根据iconPath获取图片需自行实现
        ImageIcon icon = xxxxxx;
        closeButton.setIcon(icon);
        this.closeAction = closeAction;
    }

    /**
     * 创建窗口控制按钮
     * @param iconPath 图标路径
     * @param runnable 按钮点击时执行的操作
     * @return 创建的按钮标签
     */
    private static JLabel createWindowBtn(String iconPath, Runnable runnable) {
        // 注:根据iconPath获取图片需自行实现
        ImageIcon icon = xxxxxx;
        JLabel button = new JLabel(icon);
        JLabel button = new JLabel();
        button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button.setBorder(BorderFactory.createEmptyBorder(8, 5, 8, 5));
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (null != runnable) {
                    runnable.run();
                }
            }
        });
        return button;
    }

    /**
     * 设置内容区域并添加边界调整监听器
     */
    private void setupContentArea() {
        // 创建内容面板
        JPanel contentPanel = new JPanel(new BorderLayout());
        getContentPane().add(contentPanel, BorderLayout.CENTER);
        // 创建边界调整监听器实例
        ResizeMouseListener resizeListener = new ResizeMouseListener();
        ResizeMouseMotionListener resizeMotionListener = new ResizeMouseMotionListener();
        // 为整个窗口添加边界调整监听器
        addMouseListener(resizeListener);
        addMouseMotionListener(resizeMotionListener);
        // 为内容面板添加边界调整监听器
        contentPanel.addMouseListener(resizeListener);
        contentPanel.addMouseMotionListener(resizeMotionListener);
        // 为标题面板添加边界调整监听器
        titlePanel.addMouseListener(resizeListener);
        titlePanel.addMouseMotionListener(resizeMotionListener);
        // 为根面板添加边界调整监听器
        getRootPane().addMouseListener(resizeListener);
        getRootPane().addMouseMotionListener(resizeMotionListener);
    }

    /**
     * 为面板添加拖拽功能
     * @param panel 要添加拖拽功能的面板
     */
    private void addDragFunctionality(JPanel panel) {
        MouseAdapter ma = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (resizeDirection == NONE) {
                    initialClick = e.getPoint();
                }
            }
            @Override
            public void mouseDragged(MouseEvent e) {
                if (isResizing) {
                    handleResize(e);
                } else if (resizeDirection == NONE && !maximizedDisable) {
                    // 如果窗口是最大化的,先还原再移动
                    if (isMaximized) {
                        toggleMaximize();
                        int x = e.getXOnScreen() - getWidth() / 2;
                        int y = e.getYOnScreen() - titlePanel.getHeight() / 2;
                        setLocation(x, y);
                        initialClick = new Point(getWidth() / 2, titlePanel.getHeight() / 2);
                    }
                    int thisX = getLocation().x;
                    int thisY = getLocation().y;
                    int xMoved = e.getX() - initialClick.x;
                    int yMoved = e.getY() - initialClick.y;
                    setLocation(thisX + xMoved, thisY + yMoved);
                }
            }
            @Override
            public void mouseClicked(MouseEvent e) {
                // 双击标题栏最大化/还原
                if (e.getClickCount() == 2 && resizeDirection == NONE && !maximizedDisable) {
                    toggleMaximize();
                }
            }
        };
        panel.addMouseListener(ma);
        panel.addMouseMotionListener(ma);
    }

    /**
     * 切换窗口最大化/还原状态
     */
    public void toggleMaximize() {
        if (isMaximized) {
            // 还原到正常大小
            setBounds(normalBounds);
            isMaximized = false;
            // 注:获取图片需自行实现
            ImageIcon icon = xxxxxx;
            maxButton.setIcon(icon);
        } else {
            // 保存当前位置和大小
            normalBounds = getBounds();
            originalLocation = getLocation();
            // 获取当前屏幕的最大可用边界
            GraphicsConfiguration config = getCurrentScreenDevice().getDefaultConfiguration();
            Rectangle screenBounds = config.getBounds();
            Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(config);
            int x = screenBounds.x + screenInsets.left;
            int y = screenBounds.y + screenInsets.top;
            int width = screenBounds.width - screenInsets.left - screenInsets.right;
            int height = screenBounds.height - screenInsets.top - screenInsets.bottom;
            setBounds(x, y, width, height);
            isMaximized = true;
            // 注:获取图片需自行实现
            ImageIcon icon = xxxxxx;
            maxButton.setIcon(icon);
        }
    }

    /**
     * 禁用最大化功能
     */
    public void setMaximizedDisable() {
        maximizedDisable = true;
    }

    /**
     * 设置窗口图标
     * @param imagePath 图标路径
     * @param width 图标宽度
     * @param height 图标高度
     */
    public void setIcon(String imagePath, int width, int height) {
        // 注:根据imagePath获取图片需自行实现
        ImageIcon icon = xxxxxx;
        titleLabel.setIcon(icon);
        titleLabel.setHorizontalTextPosition(SwingConstants.RIGHT);
        titleLabel.setVerticalTextPosition(SwingConstants.CENTER);
        titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 5));
        int margin = (Math.max(height, 24) - 24) / 2;
        minButton.setBorder(BorderFactory.createEmptyBorder(margin, 5, margin, 5));
        maxButton.setBorder(BorderFactory.createEmptyBorder(margin, 5, margin, 5));
        closeButton.setBorder(BorderFactory.createEmptyBorder(margin, 5, margin, 5));
        iconTitlePanel.revalidate();
        iconTitlePanel.repaint();
    }

    /**
     * 设置窗口标题
     * @param title 标题文本
     */
    @Override
    public void setTitle(String title) {
        titleLabel.setText(title);
        titleLabel.setVisible(true);
        iconTitlePanel.revalidate();
        iconTitlePanel.repaint();
    }

    /**
     * 设置窗口标题和字体
     * @param title 标题文本
     * @param font 字体
     */
    public void setTitle(String title, Font font) {
        titleLabel.setText(title);
        titleLabel.setFont(font);
        titleLabel.setVisible(true);
        iconTitlePanel.revalidate();
        iconTitlePanel.repaint();
    }

    /**
     * 设置标题栏样式
     * @param operate 自定义操作
     */
    public void setTitleStyle(BiConsumer<JPanel, JLabel> operate) {
        if (null != operate) {
            operate.accept(titlePanel, titleLabel);
        }
    }

    /**
     * 设置标题栏下划线(默认颜色)
     */
    public void setUnderLine() {
        setUnderLine(new Color(0, 183, 195));
    }

    /**
     * 设置标题栏下划线颜色
     * @param color 下划线颜色
     */
    public void setUnderLine(Color color) {
        setUnderLine(color, 1);
    }

    /**
     * 设置标题栏下划线
     * @param color 下划线颜色
     * @param thickness 下划线厚度
     */
    public void setUnderLine(Color color, int thickness) {
        titlePanel.setBorder(BorderFactory.createMatteBorder(0, 0, thickness, 0, color));
    }

    /**
     * 隐藏最小化和最大化按钮
     */
    public void hideMinMax() {
        minButton.setVisible(false);
        maxButton.setVisible(false);
    }

    /**
     * 设置圆角半径
     * @param radius 圆角半径
     */
    public void setCornerRadius(int radius) {
        this.cornerRadius = radius;
        setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), cornerRadius, cornerRadius));
        repaint();
    }

    /**
     * 边界调整大小的鼠标监听器
     */
    private class ResizeMouseListener extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            if (resizeDirection != NONE) {
                isResizing = true;
            }
        }
        @Override
        public void mouseReleased(MouseEvent e) {
            isResizing = false;
            resizeDirection = NONE;
            setCursor(Cursor.getDefaultCursor());
        }
    }

    /**
     * 边界调整大小的鼠标移动监听器
     */
    private class ResizeMouseMotionListener extends MouseMotionAdapter {
        @Override
        public void mouseDragged(MouseEvent e) {
            if (isResizing) {
                handleResize(e);
            }
        }
        @Override
        public void mouseMoved(MouseEvent e) {
            updateCursor(e.getPoint());
        }
    }

    /**
     * 更新鼠标光标
     * @param p 鼠标位置点
     */
    private void updateCursor(Point p) {
        if (isMaximized) {
            setCursor(Cursor.getDefaultCursor());
            resizeDirection = NONE;
            return;
        }
        int x = p.x;
        int y = p.y;
        int width = getWidth();
        int height = getHeight();
        boolean top = y < resizeBorder;
        boolean bottom = y > height - resizeBorder - titlePanel.getHeight();
        boolean left = x < resizeBorder;
        boolean right = x > width - resizeBorder;
        if (top && left) {
            setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
            resizeDirection = TOP_LEFT;
        } else if (top && right) {
            setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
            resizeDirection = TOP_RIGHT;
        } else if (bottom && left) {
            setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
            resizeDirection = BOTTOM_LEFT;
        } else if (bottom && right) {
            setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
            resizeDirection = BOTTOM_RIGHT;
        } else if (top) {
            setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
            resizeDirection = TOP;
        } else if (bottom) {
            setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
            resizeDirection = BOTTOM;
        } else if (left) {
            setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
            resizeDirection = LEFT;
        } else if (right) {
            setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
            resizeDirection = RIGHT;
        } else {
            setCursor(Cursor.getDefaultCursor());
            resizeDirection = NONE;
        }
    }

    /**
     * 处理调整大小
     * @param e 鼠标事件
     */
    private void handleResize(MouseEvent e) {
        if (!isResizing) {
            return;
        }
        int x = getX();
        int y = getY();
        int width = getWidth();
        int height = getHeight();
        int newX = x;
        int newY = y;
        int newWidth = width;
        int newHeight = height;
        if ((resizeDirection & LEFT) != 0) {
            newX = e.getXOnScreen();
            newWidth = width - (e.getXOnScreen() - x);
        }
        if ((resizeDirection & RIGHT) != 0) {
            newWidth = e.getXOnScreen() - x;
        }
        if ((resizeDirection & TOP) != 0) {
            newY = e.getYOnScreen();
            newHeight = height - (e.getYOnScreen() - y);
        }
        if ((resizeDirection & BOTTOM) != 0) {
            newHeight = e.getYOnScreen() - y;
        }
        int minWidth = 400;
        int minHeight = 300;
        if (newWidth < minWidth) {
            if ((resizeDirection & LEFT) != 0) {
                newX = x + width - minWidth;
            }
            newWidth = minWidth;
        }
        if (newHeight < minHeight) {
            if ((resizeDirection & TOP) != 0) {
                newY = y + height - minHeight;
            }
            newHeight = minHeight;
        }
        setBounds(newX, newY, newWidth, newHeight);
    }

    @Override
    public void setVisible(boolean visible) {
        if (visible) {
            if (!locationInitialized) {
                if (parentFrame != null) {
                    setLocationRelativeTo(parentFrame);
                } else {
                    setLocationRelativeTo(null);
                }
                locationInitialized = true;
            } else {
                if (originalLocation != null) {
                    setLocation(originalLocation);
                }
            }
            originalLocation = getLocation();
            originalSize = getSize();
        } else {
            originalLocation = getLocation();
            originalSize = getSize();
        }
        super.setVisible(visible);
    }

    /**
     * 在指定屏幕居中显示
     * @param screenDevice 目标屏幕设备
     */
    public void setLocationToScreen(GraphicsDevice screenDevice) {
        if (screenDevice != null) {
            Rectangle screenBounds = screenDevice.getDefaultConfiguration().getBounds();
            int x = screenBounds.x + (screenBounds.width - getWidth()) / 2;
            int y = screenBounds.y + (screenBounds.height - getHeight()) / 2;
            setLocation(x, y);
            currentScreenDevice = screenDevice;
            originalLocation = getLocation();
            locationInitialized = true;
        }
    }

    /**
     * 确保窗口在当前屏幕显示
     */
    public void ensureOnCurrentScreen() {
        if (originalLocation != null) {
            setLocation(originalLocation);
        } else if (currentScreenDevice != null) {
            setLocationToScreen(currentScreenDevice);
        }
    }

    /**
     * 保存当前位置信息
     */
    public void saveCurrentLocation() {
        this.originalLocation = getLocation();
        this.originalSize = getSize();
        updateCurrentScreenDevice();
    }

    /**
     * 快速显示窗口(带内容组件)
     * @param component 内容组件
     * @param closeAction 关闭回调
     */
    public void showFrame(JComponent component, Runnable closeAction) {
        add(component);
        setSize(new Dimension(1300, 800));
        setTitleStyle((titlePanel, titleLabel) -> {
            titlePanel.setOpaque(true);
            titlePanel.setBackground(Color.decode("#CDE6E6"));
            titlePanel.setBorder(BorderFactory.createEmptyBorder(5, 50, 5, 10));
            // 注:获取图片需自行实现
            ImageIcon icon = xxxxxx;
            titleLabel.setIcon(icon);
            titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 20));
        });
        hideMinMax();
        addCloseAction("icons/window/goback.png", () -> {
            if (null != closeAction) {
                closeAction.run();
            }
        });
        setMaximized(true);
        if (parentFrame != null) {
            setLocationToScreen(parentFrame.getCurrentScreenDevice());
        } else {
            ensureOnCurrentScreen();
        }
        setVisible(true);
    }

    /**
     * 创建跟随父窗口的新窗口
     * @param title 窗口标题
     * @return 新的CusFrame实例
     */
    public CusFrame createChildFrame(String title) {
        CusFrame childFrame = new CusFrame(title, this);
        childFrame.setLocationToScreen(this.getCurrentScreenDevice());
        return childFrame;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(new Color(0, 183, 195));
        g2d.setStroke(new BasicStroke(1));
        g2d.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, cornerRadius, cornerRadius);
        g2d.dispose();
    }
}

三、核心功能说明

窗口装饰:

  • setUndecorated(true):移除系统默认标题栏
  • setShape(new RoundRectangle2D.Double(...)):设置圆角矩形形状
  • paint(Graphics g):绘制圆角边框

自定义标题栏:

  • 左侧:图标 + 标题文字
  • 右侧:最小化、最大化/还原、关闭按钮
  • 支持拖拽移动窗口
  • 支持双击标题栏最大化/还原

窗口调整大小:

  • 鼠标移动到窗口边缘时自动改变光标样式
  • 支持八个方向的拖拽调整(上、下、左、右、四个角)
  • 置了最小宽度400px、最小高度300px

多屏幕适配:

  • updateCurrentScreenDevice():检测窗口当前所在的屏幕
  • setLocationToScreen():在指定屏幕居中显示
  • ensureOnCurrentScreen():确保窗口在当前屏幕内可见

窗口状态管理:

  • toggleMaximize():切换最大化/还原,最大化时排除系统任务栏区域
  • setMaximizedDisable():禁用最大化功能
  • saveCurrentLocation():保存窗口位置,用于重新显示时恢复

四、使用示例

4.1 创建基本窗口

java 复制代码
CusFrame frame = new CusFrame("我的应用");
frame.setSize(1000, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);

4.2 设置窗口图标

java 复制代码
// 注:图片获取需自行实现
frame.setIcon("icons/my_logo.png", 32, 32);

4.3 自定义标题栏样式

java 复制代码
frame.setTitleStyle((titlePanel, titleLabel) -> {
    titlePanel.setBackground(new Color(50, 50, 80));
    titleLabel.setForeground(Color.WHITE);
    titleLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 18));
});
frame.setUnderLine(new Color(100, 150, 200), 2);

4.4 禁用最大化

java 复制代码
frame.setMaximizedDisable();
frame.hideMinMax();

4.5 设置关闭回调

java 复制代码
frame.addCloseAction(() -> {
    int result = JOptionPane.showConfirmDialog(frame, "确认退出?", "提示", JOptionPane.YES_NO_OPTION);
    if (result == JOptionPane.YES_OPTION) {
        System.exit(0);
    }
});

4.6 多窗口跟随(子窗口跟随父窗口屏幕)

java 复制代码
CusFrame mainFrame = new CusFrame("主窗口");
mainFrame.setSize(1200, 800);
mainFrame.setVisible(true);

CusFrame childFrame = mainFrame.createChildFrame("子窗口");
childFrame.setSize(600, 400);
childFrame.setVisible(true);

五、注意事项

  1. 代码中涉及图标加载的地方已注释,需要读者自行实现或替换
  2. 代码中所有png图片均为实例,可自行从网上下载
  3. 圆角边框:最大化时会暂时禁用圆角(避免系统任务栏区域显示异常)
  4. 线程安全:窗口操作已在 EDT 中执行,无需额外处理
  5. 多屏幕:最大化时只在当前屏幕内最大化,不会跨屏幕
相关推荐
techdashen1 小时前
深入 Rust enum 的内存世界
开发语言·后端·rust
xuanjiong1 小时前
DDD架构Repository仓储数据流转全链路详解:Domain与其他层的数据流转
java·系统架构
吴声子夜歌1 小时前
Java——类加载机制
java·开发语言·python
Xiacqi11 小时前
Java 中 String、StringBuffer、StringBuilder 的区别
java
龙码精神1 小时前
TimescaleDB 物联网设备属性历史数据表设计及常用SQL文档
后端
Xiacqi11 小时前
Java 常用集合框架手册
java
笨蛋不要掉眼泪2 小时前
Java并发编程:线程的创建和运行
java·开发语言·jvm
九伯都2 小时前
java编写 agent 入门案例
java·开发语言
环流_2 小时前
redis:持久化rdb
java·数据库·redis