Swing自定义标题栏

文章目录

  • Swing自定义标题栏
    • 需求
    • 最终效果如图
    • 步骤
    • 额外需求

Swing自定义标题栏

需求

想要实现IDEA类似的标题栏效果,菜单栏放在标题栏同一行,标题居中,右侧为按钮。如图:

在这里插入图片描述

最终效果如图

Swing自定义标题栏_第1张图片

步骤

使用依赖FlatLaf


<dependency>
    <groupId>com.formdevgroupId>
    <artifactId>flatlafartifactId>
    <version>3.2.5version>
dependency>
  1. 取消系统标题栏

    frame.setUndecorated(true); 即可实现。

  2. 自定义标题栏

    class CustomTitlePane extends JMenuBar {
    		public CustomTitlePane(JFrame frame) {
    
    			// 创建一个空白面板来添加左侧间距
    			JLabel blankLbl = new JLabel("   ");
    			add(blankLbl);
    
    			// 添加图标
    			Image image = ImageManager.getImage("/images/logo.png");
    			ImageIcon icon = new ImageIcon(image);
    			Image scaledImage = icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH);
    			JLabel iconLabel = new JLabel(new ImageIcon(scaledImage));
    			add(iconLabel);
    
    			// 创建一个空白面板来添加左侧间距
    			JLabel blankLbl2 = new JLabel("   ");
    			add(blankLbl2);
    
    			JMenuBar menuBar = ToolkitUtilities.getMenuBar();
    			add(menuBar);
    
    			// 创建并添加标题标签,居中对齐
    			JLabel titleLabel = new JLabel(frame.getTitle());
    			titleLabel.setHorizontalAlignment(SwingConstants.CENTER);
    			add(Box.createHorizontalGlue());
    			add(titleLabel);
    			add(Box.createHorizontalGlue());
    
    			// 添加最小化按钮
    			JButton minimizeButton = new JButton("-");
    			minimizeButton.addActionListener(new ActionListener() {
    				@Override
    				public void actionPerformed(ActionEvent e) {
    					frame.setExtendedState(JFrame.ICONIFIED);
    				}
    			});
    			add(minimizeButton);
    
    			// 添加最大化按钮
    			JButton maximizeButton = new JButton("[]");
    			maximizeButton.addActionListener(new ActionListener() {
    				@Override
    				public void actionPerformed(ActionEvent e) {
    					if (frame.getWidth() == screenSize.width) {
    						frame.setSize(new Dimension(1280, 768));
    						frame.setLocationRelativeTo(null);
    					} else {
    						frame.setBounds(0, workArea.y, screenSize.width, workArea.height);
    					}
    				}
    			});
    			add(maximizeButton);
    
    			// 添加关闭按钮
    			JButton closeButton = new JButton("X");
    			closeButton.addActionListener(new ActionListener() {
    				@Override
    				public void actionPerformed(ActionEvent e) {
    					frame.dispose();
    				}
    			});
    			add(closeButton);
    		}
    
    		@Override
    		public void updateUI() {
    			super.updateUI();
    
    			// 设置菜单栏的外观,使用FlatMenuBarUI以匹配FlatLaf主题
    			setUI(new FlatMenuBarUI());
    		}
    	}
    
  3. 自定义窗口拖放与调整大小

    1. 首先提供getResizeCursor方法,根据鼠标当前位置来判断指针当前应该是什么形状,这里针对需求来说只有两种:默认指针和调整大小的指针。
    private static int getResizeCursor(JFrame frame, Point p) {
            Insets insets = frame.getInsets();
            int x = p.x;
            int y = p.y;
            int width = frame.getWidth();
            int height = frame.getHeight();
    
            if (x < BORDER_SIZE && y < BORDER_SIZE) {
                return Cursor.NW_RESIZE_CURSOR;
            }
            if (x < BORDER_SIZE && y > height - BORDER_SIZE) {
                return Cursor.SW_RESIZE_CURSOR;
            }
            if (x > width - BORDER_SIZE && y < BORDER_SIZE) {
                return Cursor.NE_RESIZE_CURSOR;
            }
            if (x > width - BORDER_SIZE && y > height - BORDER_SIZE) {
                return Cursor.SE_RESIZE_CURSOR;
            }
            if (x < BORDER_SIZE) {
                return Cursor.W_RESIZE_CURSOR;
            }
            if (x > width - BORDER_SIZE) {
                return Cursor.E_RESIZE_CURSOR;
            }
            if (y < BORDER_SIZE) {
                return Cursor.N_RESIZE_CURSOR;
            }
            if (y > height - BORDER_SIZE) {
                return Cursor.S_RESIZE_CURSOR;
            }
            return Cursor.DEFAULT_CURSOR;
        }
    
    1. 这里针对鼠标添加四类事件监听器来实现:

      • 鼠标移动 mouseMoved

        鼠标移动时根据上面的方法判断当前指针所处位置,及时更新鼠标指针图标。

      • 鼠标点击 mousePressed

        鼠标点击时,根据当前指针图标来判断:

        ​ 如果是默认图标则认为是拖动操作,需要记录当前的坐标;

        ​ 如果是其它图标则认为是调整大小操作。

      • 鼠标释放 mouseReleased

        鼠标释放时需要变回默认指针。

      • 鼠标拖动 mouseDragged

        鼠标拖动时,根据当前指针图标判断可以分为两种情况:

        ​ 如果是默认图标,则认为是移动位置操作,通过setLocation方法修改frame位置;

        ​ 如果不是默认图标,则认为是调整大小操作,对于调整大小操作,需要区分当前指针具体类型来判断是哪个方向上的位移,同时需要计算当前指针坐标点与原始指针坐标点之间的距离,因为在调整大小的过程中除了size改变还有location的改变。

        ​ 同时,如果要限定窗口最小的size,我也是在这里通过MIN_WIDTHMIN_HEIGHT做的限制。

    private static void addResizeWindowSupport(JFrame frame) {
            frame.addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    // 获取当前指针形状
                    cursor = getResizeCursor(frame, e.getPoint());
                    // 如果是默认指针,可能是要做拖动操作
                    if(cursor == Cursor.DEFAULT_CURSOR) {
                        mouseAtX = e.getPoint().x;
                        mouseAtY = e.getPoint().y;
                    }
                }
    
                @Override
                public void mouseReleased(MouseEvent e) {
                    // 释放指针,变回默认指针
                    cursor = Cursor.DEFAULT_CURSOR;
                }
            });
    
            frame.addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    // 指针拖动过程中,如果不是默认指针,则是在变大小
                    if (cursor != Cursor.DEFAULT_CURSOR) {
                        Point p = e.getPoint();
                        SwingUtilities.convertPointToScreen(p, frame);
                        Point newLocation = frame.getLocation();
                        Dimension newSize = frame.getSize();
    
                        switch (cursor) {
                            case Cursor.N_RESIZE_CURSOR:
                                newSize.height = frame.getHeight() - p.y + frame.getLocation().y;
                                newLocation.y = p.y;
                                break;
                            case Cursor.S_RESIZE_CURSOR:
                                newSize.height = p.y - frame.getLocation().y;
                                break;
                            case Cursor.W_RESIZE_CURSOR:
                                newSize.width = frame.getWidth() - p.x + frame.getLocation().x;
                                newLocation.x = p.x;
                                break;
                            case Cursor.E_RESIZE_CURSOR:
                                newSize.width = p.x - frame.getLocation().x;
                                break;
                            case Cursor.NW_RESIZE_CURSOR:
                                newSize.height = frame.getHeight() - p.y + frame.getLocation().y;
                                newLocation.y = p.y;
                                newSize.width = frame.getWidth() - p.x + frame.getLocation().x;
                                newLocation.x = p.x;
                                break;
                            case Cursor.NE_RESIZE_CURSOR:
                                newSize.height = frame.getHeight() - p.y + frame.getLocation().y;
                                newLocation.y = p.y;
                                newSize.width = p.x - frame.getLocation().x;
                                break;
                            case Cursor.SW_RESIZE_CURSOR:
                                newSize.height = p.y - frame.getLocation().y;
                                newSize.width = frame.getWidth() - p.x + frame.getLocation().x;
                                newLocation.x = p.x;
                                break;
                            case Cursor.SE_RESIZE_CURSOR:
                                newSize.height = p.y - frame.getLocation().y;
                                newSize.width = p.x - frame.getLocation().x;
                                break;
                        }
                        frame.setBounds(newLocation.x, newLocation.y, Math.max(100, newSize.width), Math.max(100, newSize.height));
                    } else {
                        // 如果是默认指针,则是拖动
                        frame.setLocation(e.getXOnScreen() - mouseAtX, e.getYOnScreen() - mouseAtY);
                    }
                }
            });
    
            frame.addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    frame.setCursor(Cursor.getPredefinedCursor(getResizeCursor(frame, e.getPoint())));
                }
            });
        }
    

额外需求

  1. 默认窗口最大化且不占用任务栏空间

    如果不做操作,默认的最大化会导致任务栏被遮盖。这里上点手段:

    // 获取屏幕尺寸
    screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    // 获取屏幕工作区域的尺寸
    workArea = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
    // 设置窗口的初始位置和尺寸,确保底部不占用任务栏空间
    frame.setBounds(0, workArea.y, screenSize.width, workArea.height);
    

你可能感兴趣的:(Java,开发语言,java)