Swing自定义标题栏

news2024/11/24 16:23:11

文章目录

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

Swing自定义标题栏

需求

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

在这里插入图片描述

最终效果如图

在这里插入图片描述

步骤

使用依赖FlatLaf

<!-- https://mvnrepository.com/artifact/com.formdev/flatlaf -->
<dependency>
    <groupId>com.formdev</groupId>
    <artifactId>flatlaf</artifactId>
    <version>3.2.5</version>
</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);
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1167886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚拟串口软件使用介绍

对于上位机开发来说(特别是串口通信应用),上机位软件的调试尤为重要,但是上机位软件的调试并不关心硬件,只需要关注验证发送的数据帧的接收情况,为了便于调试,可以将上机位软件与串口软件互通,实现数据的交互,但由于互通需要串口,可以借助串口虚拟软件(VSPD),虚拟出…

lazada商品评论API接口(评论内容|日期|买家昵称|追评内容|评论图片|评论视频..)

Lazada商品评论API接口是Lazada开放平台提供的一种API接口&#xff0c;可以帮助开发者获取Lazada平台上的商品评论数据。 通过该接口&#xff0c;开发者可以获取到用户对商品的评论信息&#xff0c;包括评论内容、评价等级、评论时间等&#xff0c;从而了解用户对商品的反馈和…

用android studio调试react native中的原生代码(windows+android)

要用Android Studio调试React Native原生代码&#xff0c; 1. 需要先在终端中运行react-native start命令启动React Native服务器。 2. 然后&#xff0c;在Android Studio中打开你的React Native项目&#xff08;\android\build.gradle&#xff09;&#xff0c;连接你的设备或…

修改YOLOv5的模型结构

YOLOv5 模型结构 C3模块结构图 修改目标 修改目标是移除C3模块concat后的卷积操作 YOLOv5的模型存储在项目目录下的models目录中。 一些以yaml为后缀的文件保存了一些模型的超参数&#xff0c;通过不同的参数&#xff0c;形成了yolov5s,yolov5n,yolov5l等不同参数等级&#…

【java零基础入门到就业】第四天:Notepad++软件的下载和安装

文章目录 Notepad软件简介下载安装 Notepad软件 简介 Notepad&#xff08;又称Notepad Plus Plus&#xff09;是一款免费的文本编辑器&#xff0c;是Windows操作系统下非常受欢迎的开源软件。它提供了许多强大的功能&#xff0c;适合用于编写各种编程语言的源代码、编辑文本文…

【vscode】Window11环境下vscode使用Fira Code字体【教程】

【vscode】Window11环境下vscode使用Fira Code字体【教程】 文章目录 【vscode】Window11环境下vscode使用Fira Code字体【教程】1. 下载Fira Code字体2. 安装Fira Code字体3. 配置vscode4. 效果如下Reference 如果想要在Ubuntu环境下使用Fira Code字体&#xff0c;可以参考我的…

IP路由配置

一、路由协议分类 路由协议是路由器之间维护路由表的规则,用于发现路由并生成路由表以指导报文转发。可分为: 通过链路层协议发现的直连路由通过网络管理员手动配置的静态路由通过动态路由协议发现的动态路由其中,动态路由根据作用范围分为: 内部网关协议(IGP):包括rip…

linux系统SQL server数据库定时收缩

问题现象 出现下图问题&#xff0c;导致连接该数据库的程序不能正常启动 解决办法 定时收缩数据库 数据库定时收缩脚本 需要三个脚本文件 linux_sqlcmd_timing_task_shrink.sh&#xff1a;主脚本文件 # 设置数据库名称、用户名、密码等信息 # db_name"volador"…

OpenHarmony 入门——搭建OpenHarmony本地应用开发环境

文章大纲 引言一、应用开发工具下载SDK替换1、本地SDK目录查找2、OpenHarmony SDK编译3、SDK替换4、sdk node_modules依赖包下载5、验证 三、镜像烧写工具及指南1、下载烧录工具2、安装usb驱动3、rk3568镜像编译5、 镜像配置&烧录 引言 OpenHarmony是由开放原子开源基金会…

Unity中Shader的烘培分支的判断

文章目录 前言一、上一篇文章中所需要的 lightmapUV 只有在烘焙时才会使用1、查看帮助文档后&#xff0c;Unity中判断烘培是否开启&#xff0c;使用的是LIGHTMAP_ON2、我们在 appdata 和 v2f 中&#xff0c;定义第二套UV 前言 Unity中Shader的烘培分支的判断&#xff0c;基于上…

AITO问界崛起的“临门一脚”,落在了赛力斯汽车的智慧工厂里

文 | 智能相对论 作者 | 沈浪 AITO问界新M7的销量爆了&#xff0c;口碑也紧接着“爆”了。 AITO问界新M7系列上市以来50天&#xff0c;累计大定突破8万辆。AITO问界M9预计今年12月上市&#xff0c;预订超过了1.5万辆。根据最新公布的产销数据&#xff0c;在过去的10月份&…

Contec SolarView Compact < 6.00 远程命令执行漏洞 (CVE-2023-23333)

Contec SolarView Compact < 6.00 远程命令执行漏洞 &#xff08;CVE-2023-23333&#xff09; 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: body"SolarView Compact" 漏洞复现1. 构造poc2. 执行命令id命令pwd命令 免责声明 仅用于技术交流,目的是向相关安全人…

c++qt学习对象树

1.当创建的对象在堆区时候&#xff0c;如果指定的父亲是QObject派生下来的类或者QObject子类派生下来的类&#xff0c;可以不用管理释放的操作&#xff0c;将对象会放在对象树中。 2.一定程度上简化了内存回收机制 构造顺序与析构顺序相反

Java实现驼峰命名的字符串转化

目录 一、场景描述 二、代码示例 1、下划线大写方式命名的字符串转换为驼峰式 2、驼峰式命名的字符串转换为下划线大写的方式 3、完整代码 一、场景描述 在开发场景中&#xff0c;我们会遇到一些涉及字符串的转化。例如&#xff1a;数据库字段的名称叫TYPE_NAME&#xff0c…

Verilog刷题[hdlbits] :Module addsub

题目&#xff1a;Module addsub An adder-subtractor can be built from an adder by optionally negating one of the inputs, which is equivalent to inverting the input then adding 1. The net result is a circuit that can do two operations: (a b 0) and (a ~b …

2023/11/3 JAVA学习

默认执行toString方法 在类中重写toString方法,会自动写 默认比较的是对象的地址 equals主要也是为了重写 克隆必须要Cloneable标记接口,虚拟机才会允许克隆 调用对象中的克隆方法时会报错(异常),按住ctrl 回车扔掉异常 如果用对象自己的equals方法,如果本身是null会报错 效率…

内审数字化:防范化解企业风险的灵丹妙药

某家制造业公司的审计部负责人王经理&#xff0c;最近对公司的各种风险感到焦虑。供应商管理、招标采购、市场竞争激烈、法规变化等因素都可能对他的公司构成威胁。如何防范化解这些风险&#xff0c;成为王经理的首要任务。 正当王经理一筹莫展时&#xff0c;一次偶然的机会&a…

【软著写作】软著写作过程记录

文章目录 整体流程图&#xff1a;写在前面&#xff1a;一、准备材料1 准备材料2 申请盖章 二、软件登记1 注册账号2 填报软著 整体流程图&#xff1a; 写在前面&#xff1a; 这两天填报了一篇软著&#xff0c;正好将以前第一次填报时&#xff0c;踩的一些坑和过程记录了一下&am…

ngxin 正向代理配置 访问互联网

如果本地无法访问互联网&#xff0c;则通过nginx的正向代理&#xff0c;可以将本地的所有流量进行代理&#xff0c;从而达到访问互联网的目的&#xff0c;话不多说&#xff0c;上代码&#xff1a; 1、下载nginx并安装相关依赖 yum -y install gcc yum install -y gcc-c yum …

Nacos报错Connection refused (Connection refused)(最后原因醉了,非常醉)

目录 一、问题产生二、排查思路1.nacos拒绝连接&#xff0c;排查思路&#xff1a;2.Nacos启动成功但是拒绝连接的几种原因&#xff1a; 三、实操过程&#xff08;着急解决问题直接看这个&#xff09;1.启动Nacos2.查看Nacos启动日志3.根据日志处理问题4.修改Nacos5.重启Nacos 一…