文章目录
- 前言
- 一、基本组件 + GUI编程逻辑套路(套路重要!!!!)
-
- 1 最外层的窗体 JFrame(重要,这里面会将GUI编程的套路,这个套路很重要)
- 2 最上层的菜单 JMenuBar(菜单栏) -> JMenu(选项) -> JMenuItem(选项条目)
- 3 Layout布局(这个了解一下就行,最后会介绍JFormDesigner这个设计师使用)
- 4 管理文字和图片的容器 JLabel
- 5 JTextPane文本组件(结合6一起看)
- 6 JScrollPane滚动组件(结合5一起看)
- 7 JButton(按钮)
- 8 JTextField 单行输入文本框(结合事件一起看要)
- 8 JRadioButton(单选框)--- 与ButtonGroup一起用
- 9 JCheckBox(复选框)
- 10 JComboBox(单选下拉列表框)
- 11 JTree 组件
- 12 JList 组件 : 可以用来做多选框和单选框
- 13 消息提示框(常用)
- 14 消息对话框(常用)
- 小总结
- 二、事件
- 三、方法引用在Swing中的使用(建议先学完方法引用在看这个更加好理解)---- 套路4
- 三、使用第三方FlatLaf jar包美化Swing组件
- 四、JFormDesigner设计师的使用
- 五、给JLables设置图片的随着窗口缩放背景一起缩放的解决方案
- 六、拼图小游戏开发
前言
由于本节博客是讲Java里面的Swing里面的gui编程,但现在gui的环境大家也知道。并且Java本身在gui编程的选择上Java算是已经完全放弃这一块(Java的优势主要在于web开发),就留下了一个比较难用的Swing,Python好歹还有pyqt这些大型的qt开发框架。所以本文我们不会对Swing做一个过多的详细介绍。只会以一个实际的拼图小游戏项目来简单用用Swing的一些常用组件。
建议学完Java的字典集合的api使用还有Java文件操作再来看这个。没有字典可以用,数据驱动、数据管理真的好麻烦用列表。
本文前面几个组件的讲述为了讲述gui编程的一般套路,会以拼图小游戏为例子讲述;后面部分组件就纯粹只讲述简单使用了。
拼图小游戏游戏界面如下:
【注】:Java的gui编程基本已经算放弃了,如果真对gui有兴趣,不妨去学Python的pyqt5,组件更加丰富。java的gui编程了解一下Swing怎么用就可以了,真有需求时能快速通过这篇博客捡起来就可以了。
一、基本组件 + GUI编程逻辑套路(套路重要!!!!)
1 最外层的窗体 JFrame(重要,这里面会将GUI编程的套路,这个套路很重要)
(1)JFrame窗体的创建、set尺寸、展示
需求:
package cn.hjblogs.ui;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
// 1.创建游戏主窗口
JFrame gameJFrame = new JFrame();
// 设置窗口尺寸
gameJFrame.setSize(603,680);
//展示窗口
gameJFrame.setVisible(true);
// 2.创建一个登录界面窗口
JFrame loginJFrame = new JFrame();
loginJFrame.setSize(488,430);
loginJFrame.setVisible(true);
// 3.创建一个注册界面窗口
JFrame registerJFrame = new JFrame();
registerJFrame.setSize(488,500);
registerJFrame.setVisible(true);
}
}
可以看到,这样就生成了3个不同窗体了啊!
但是,应该发现了,3个窗体功能各不相同,有各自的逻辑代码,如果全放在Test类里面显的很混乱。
很自然的我们想到就要分出去了,这就涉及到了GUI编程的一般套路了,下面具体演示这个套路。
(2)GUI编程分文件套路1(重要重要!!!!!!!!)— 结合套路3(事件哪里)才是最终版,看完这个立马看套路3
具体套路如下:
- 对于每一个窗口,我们单独创建一个对应的类,并且继承窗口类(Java里面就是继承JFrame),在构造方法内进行必要的初始化
具体细节要怎么做看下面代码还是要 - 对于测试类,我们改成app类,作为GUI运行的主接口所在
还是以上面的需求为例,下面开始该代码
目录如下:
对于App程序启动的入口可以设置在任意目录下,一般的是在gui下面有一个Test类(单独的gui测试)和项目文件夹下有一个App程序启动的入口类。
package cn.hjblogs.ui;
import javax.swing.*;
public class GameJFrame extends JFrame {
// 功能:游戏窗口
// 上下左右移动的代码逻辑
// 统计步数的代码逻辑
// 一键通关
// 重新开始
// 退出游戏
// 查看最终效果 等等功能
// 在构造方法中初始化窗口
public GameJFrame() {
// 设置窗口尺寸
this.setSize(603,680);
//展示窗口
this.setVisible(true);
}
}
package cn.hjblogs.ui;
import javax.swing.*;
public class LoginJFrame extends JFrame {
// 功能:登录窗口
// 获取用户输入的用户名和密码
// 生成一个验证码
// 获取用户输入的验证码
// 比较用户名、密码、验证码是否正确
// 。。。
// 在构造方法中初始化窗口
public LoginJFrame() {
// 设置窗口尺寸
this.setSize(488,430);
//展示窗口
this.setVisible(true);
}
}
package cn.hjblogs.ui;
import javax.swing.*;
public class RegisterJFrame extends JFrame {
// 功能:注册窗口
// 获取用户输入的用户名和密码
// 获取用户输入的密码(两次)
// 比较两次密码是否一致
// 判断当前用户是否已经注册
// 。。。
// 在构造方法中初始化窗口
public RegisterJFrame() {
// 设置窗口尺寸
this.setSize(488,500);
//展示窗口
this.setVisible(true);
}
}
package cn.hjblogs.ui;
public class App {
public static void main(String[] args) {
// 表示程序启动的入口
// 如果我们要开启一个界面,就创建对应的对象就可以了
GameJFrame gameJFrame = new GameJFrame(); // 创建游戏界面对象
LoginJFrame loginJFrame = new LoginJFrame(); // 创建登录界面对象
RegisterJFrame registerJFrame = new RegisterJFrame(); // 创建注册界面对象
}
}
(3)JFrame窗体的一些其他方法
讲了一般的套路,下面我们又回到具体的组件里面来学习。
package cn.hjblogs.ui;
import javax.swing.*;
public class GameJFrame extends JFrame {
// 在构造方法中初始化窗口
public GameJFrame() {
// 设置窗口尺寸
this.setSize(603,680);
// 设置窗口标题
this.setTitle("拼图小游戏 v1.0");
// 设置界面置顶(一直在最上面,点击其他软件界面不会覆盖这个界面)
this.setAlwaysOnTop(true);
// 设置界面居中
this.setLocationRelativeTo(null);
// 设置窗口关闭方式:窗口关闭时,程序结束(虚拟机也结束)
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// DO_NOTHING_ON_CLOSE 表示点击关闭按钮时不做任何事情(无法叉掉界面),界面关闭不了了
// HIDE_ON_CLOSE 表示点击关闭按钮时隐藏界面(界面不见了,但是程序还在运行),默认模式
// DISPOSE_ON_CLOSE 表示如果有多个界面的话,点击关闭最后一个界面时,程序才结束(需要注意的是所有界面都要设置这个关闭方式才行,只要有一个界面没设置,就还是默认模式HIDE_ON_CLOSE)
// EXIT_ON_CLOSE 表示点击关闭按钮时结束程序(虚拟机也结束)--- 一般用这个,并且多个界面的话,一旦这个界面关闭,其他界面也会关闭,Java虚拟机也会结束
//展示窗口,放在最后要
this.setVisible(true);
}
}
(4)获取JFame窗口的侧边边距
由于JFrame 的 setSize 方法设置的是整个窗口的尺寸,包括菜单栏、边框、标题栏和内容区域。因此,如果你在 JFrame 中添加了菜单栏,它会占用一部分窗口的高度,而 setSize 指定的高度将包括菜单栏的高度。
这就导致了,我们在layout里面设计各个组件的位置时,必须要知道顶部占据了多少个像素已经才好进行计算设计各个组件位置大小。所以这里我觉得有必要说一下。
package cn.hjblogs.ui;
import javax.swing.*;
import java.awt.*;
public class Demo {
public static void main(String[] args) {
JFrame frame = new JFrame("JTextField Example");
frame.setSize(300, 300);
frame.setVisible(true);
// 获取 Insets,这个代码必须放在 frame.setVisible(true); 之后才能获取到正确的值,不然会是 0。
Insets insets = frame.getInsets();
System.out.println("Insets top: " + insets.top); // 31
}
}
【注】: 获取 Insets,这个代码必须放在 frame.setVisible(true); 之后才能获取到正确的值,不然会是 0。
Insets 类表示窗口的边距,具体解释如下:
-
insets.top: 这个值表示窗口顶部的内边距高度。包括标题栏、菜单栏(如果存在),以及任何系统装饰的空间。
-
insets.left: 这个值表示窗口左侧的内边距宽度。通常,它包含窗口边框的宽度。
-
insets.bottom: 这个值表示窗口底部的内边距高度。包括窗口底边的装饰区域,如底部的边框或窗口装饰。
-
insets.right: 这个值表示窗口右侧的内边距宽度。通常,它包含窗口边框的宽度。
这些边距值是窗口装饰的组成部分,不包括在窗口内容区域内。
2 最上层的菜单 JMenuBar(菜单栏) -> JMenu(选项) -> JMenuItem(选项条目)
技巧1 :JMenuBar(菜单栏)可以直接添加JMenuItem(选项条目)
技巧2: JMenu(选项)可以继续添加JMenu(选项)
技巧3:JMenu(选项)没有监听事件,JMenuItem(选项条目)有监听事件
技巧4:结合JPopupMenu这种动态菜单能实现点击某个按钮或者其他弹出下拉框或者选项的那种效果
【注】:上述所有技巧在拼图小游戏中都会展示出来(都是用来解决一些问题的,这个就得积累经验才行)
(1)菜单JMenuBar(菜单栏) -> JMenu(选项) -> JMenuItem(选项条目)的基本使用
上图的三个组件就是菜单栏的一般模式了,下面在代码中演示怎么使用。(在那个界面下面要菜单代码就写在那个界面下面,方便代码管理)
菜单需求如下图片演示:
package cn.hjblogs.ui;
import javax.swing.*;
public class GameJFrame extends JFrame {
// 在构造方法中初始化窗口
public GameJFrame() {
// 设置窗口尺寸
this.setSize(603,680);
// 设置窗口标题
this.setTitle("拼图小游戏 v1.0");
// 设置界面置顶(一直在最上面,点击其他软件界面不会覆盖这个界面)
this.setAlwaysOnTop(true);
// 设置界面居中
this.setLocationRelativeTo(null);
// 设置窗口关闭方式:窗口关闭时,程序结束(虚拟机也结束)
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// DO_NOTHING_ON_CLOSE 表示点击关闭按钮时不做任何事情(无法叉掉界面),界面关闭不了了
// HIDE_ON_CLOSE 表示点击关闭按钮时隐藏界面(界面不见了,但是程序还在运行),默认模式
// DISPOSE_ON_CLOSE 表示如果有多个界面的话,点击关闭最后一个界面时,程序才结束(需要注意的是所有界面都要设置这个关闭方式才行,只要有一个界面没设置,就还是默认模式HIDE_ON_CLOSE)
// EXIT_ON_CLOSE 表示点击关闭按钮时结束程序(虚拟机也结束)--- 一般用这个(最先的主界面设置这个),并且多个界面的话,一旦这个界面关闭,其他界面也会关闭,Java虚拟机也会结束
// 初始化菜单栏
// 创建整个菜单对象
JMenuBar jMenuBar = new JMenuBar();
// 创建菜单上两个选项的对象(功能、关于我们)
JMenu functionJMenu = new JMenu("功能");
JMenu aboutUsJMenu = new JMenu("关于我们");
// 创建选项下面条目的对象
JMenuItem restartJMenuItem = new JMenuItem("重新开始");
JMenuItem reloginJMenuItem = new JMenuItem("重新登录");
JMenuItem closeJMenuItem = new JMenuItem("关闭游戏");
JMenuItem accountJMenuItem = new JMenuItem("公众号");
// 将每一个选项下面的条目添加到选项中
functionJMenu.add(restartJMenuItem);
functionJMenu.add(reloginJMenuItem);
functionJMenu.add(closeJMenuItem);
aboutUsJMenu.add(accountJMenuItem);
// 将菜单里面的两个选项添加到菜单栏中
jMenuBar.add(functionJMenu);
jMenuBar.add(aboutUsJMenu);
// 将菜单栏添加到窗口中,给整个界面设置菜单栏
this.setJMenuBar(jMenuBar);
//展示窗口,放在最后要
this.setVisible(true);
}
}
有没有觉得一堆代码全部写在构造方法里面很繁琐啊?抽取出来,初始化什么东西就抽取出来一个初始化方法就可以了
(2)GUI编程套路2:学会抽取代码(重要重要!!!!!)
idea抽取代码快捷键:选中代码 Ctrl + Alt + M (如果快捷键没有反应,选中代码下面会弹出一个提取的选项,点击也是可以的)
有没有觉得一堆代码全部写在构造方法里面很繁琐啊?抽取出来,初始化什么东西就抽取出来一个初始化方法就可以了,一层一层往下面抽取,什么功能就抽取成什么方法就可以了
package cn.hjblogs.ui;
import javax.swing.*;
public class GameJFrame extends JFrame {
// 在构造方法中初始化窗口
public GameJFrame() {
// 初始化主JFrame窗口
this.initJFrame();
// 初始化菜单栏
this.initJMenuBar();
//展示窗口,放在最后要
this.setVisible(true);
}
private void initJMenuBar() {
// 初始化菜单栏
// 创建整个菜单对象
JMenuBar jMenuBar = new JMenuBar();
// 创建菜单上两个选项的对象(功能、关于我们)
JMenu functionJMenu = new JMenu("功能");
JMenu aboutUsJMenu = new JMenu("关于我们");
// 创建选项下面条目的对象
JMenuItem restartJMenuItem = new JMenuItem("重新开始");
JMenuItem reloginJMenuItem = new JMenuItem("重新登录");
JMenuItem closeJMenuItem = new JMenuItem("关闭游戏");
JMenuItem accountJMenuItem = new JMenuItem("公众号");
// 将每一个选项下面的条目添加到选项中
functionJMenu.add(restartJMenuItem);
functionJMenu.add(reloginJMenuItem);
functionJMenu.add(closeJMenuItem);
aboutUsJMenu.add(accountJMenuItem);
// 将菜单里面的两个选项添加到菜单栏中
jMenuBar.add(functionJMenu);
jMenuBar.add(aboutUsJMenu);
// 将菜单栏添加到窗口中,给整个界面设置菜单栏
this.setJMenuBar(jMenuBar);
}
private void initJFrame() {
// 设置窗口尺寸
this.setSize(603,680);
// 设置窗口标题
this.setTitle("拼图小游戏 v1.0");
// 设置界面置顶(一直在最上面,点击其他软件界面不会覆盖这个界面)
this.setAlwaysOnTop(true);
// 设置界面居中
this.setLocationRelativeTo(null);
// 设置窗口关闭方式:窗口关闭时,程序结束(虚拟机也结束)
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// DO_NOTHING_ON_CLOSE 表示点击关闭按钮时不做任何事情(无法叉掉界面),界面关闭不了了
// HIDE_ON_CLOSE 表示点击关闭按钮时隐藏界面(界面不见了,但是程序还在运行),默认模式
// DISPOSE_ON_CLOSE 表示如果有多个界面的话,点击关闭最后一个界面时,程序才结束(需要注意的是所有界面都要设置这个关闭方式才行,只要有一个界面没设置,就还是默认模式HIDE_ON_CLOSE)
// EXIT_ON_CLOSE 表示点击关闭按钮时结束程序(虚拟机也结束)--- 一般用这个(最先的主界面设置这个),并且多个界面的话,一旦这个界面关闭,其他界面也会关闭,Java虚拟机也会结束
}
}
这样构造方法一下子就清爽了,这里我们抽取出了一个初始化当前界面和一个初始化菜单栏的init方法,这是一种套路。
3 Layout布局(这个了解一下就行,最后会介绍JFormDesigner这个设计师使用)
pyqt5Layout 布局说明
可以考一下这个pyqt5Layout 布局说明,虽然有区别,但是大致是差不多的。只不过在Swing中用JPanel纯代码控制界面布局没有python中那么好用罢了。
(1)基本说明
Java里面的布局Layout是一个隐藏的容器,不用像Python qt哪些还要自己实例化添加到界面里面去。
- 这个容器其实就是JPanel,这是什么东西?
如果学过python中的Pyqt5就知道在其中有水平布局、垂直布局这些布局对象,我们可以将其他组件放到这些里面去。JPanel就是Swing中的布局对象,但是其用代码控制并没有python中的水平布局和垂直布局那么好代码控制。所以在Swing中关于布局的设置我是不太建议使用代码控制的(这个太浪费时间,不值得)。我们采用的是与Pyqt5中的qt designer这种工具太生成界面,而在Swing中,IDEA也给我们提供了类似的工具(Swing UI设计器),等下会讲到。
下面的大红方框就是布局所在的位置,我们添加除菜单以外的组件都是添加到这个位置。
.setBounds(100, 60, 150, 30);几乎每个组件都有这个方法,x=100, y=60, width=150, height=30,前两个参数是左上角坐标,后两个坐标是组件的宽度和高度,单位是像素点。
(2)布局方式(不建议使用)
关于Swing里面的布局有哪些:可以参考Swing的几种布局
但是由于目前的软件越来越复杂,使用固定死的布局方式不灵活,因此建议自定义布局(通常会先在纸上自己先画出来,计算好各个组件的像素),自己用.setBounds(100, 60, 150, 30);这些方法慢慢调。(很显然,这不是个好想法)
【注】:自定义组件位置大小.setBounds(100, 60, 150, 30);必须先取消默认布局才能生效,即下面这行代码
JFrame frame = new JFrame();
// 设置绝对布局
frame.setLayout(null); // 操作的是最外层的JPanel布局容器
4 管理文字和图片的容器 JLabel
了解JLabel前,需要将JLabel理解成一个矩形区域,我们需要将文本或者图像放进这个矩形区域才行
JLabel:主要是一个图片容器,至于文字的有更加强大工具。所以JLabel今后我们将其理解成专门管理图片的就行
JLabel显示文本
实际上JLabel显示管理文本功能比较单一,后面还有更加强大的组件管理文本,可编辑,可以滚动的那种。后面慢慢介绍,这里先看这种比较简单的用法吧
package cn.hjblogs.ui;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
public class App {
public static void main(String[] args) {
JFrame frame = new JFrame("JLabel Example");
frame.setSize(300, 200);
frame.setDefaultCloseOperation