1 import javax.swing.*;
2 import java.awt.Color;
3 import java.awt.Font;
4 import java.awt.Graphics;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.util.Random;
8
9 public class LoginFrame extends JFrame {
10 private JTextField usernameField;//用户名文本框
11 private JPasswordField passwordField;//密码文本框
12 private JTextField captchaField;//验证码文本框
13 private CaptchaPanel captchaPanel;//自定义面板
14
15 public LoginFrame() {
16 // 设置窗口标题
17 setTitle("登录");
18
19 // 创建用户名和密码的标签和文本框
20 JLabel usernameLabel = new JLabel("用户名:");
21 JLabel passwordLabel = new JLabel("密码:");
22 usernameField = new JTextField(20);//用户名文本框大小20字符
23 passwordField = new JPasswordField(20);//密码输入文本框20字符
24
25 // 创建验证码的标签、文本框和面板
26 JLabel captchaLabel = new JLabel("验证码:");
27 captchaField = new JTextField(10);//验证码文本框10字符
28 captchaPanel = new CaptchaPanel();//生成面板
29
30 // 创建登录和注册按钮
31 JButton loginButton = new JButton("登录");
32 JButton registerButton = new JButton("注册");
33
34 //设置容器的布局管理器。
35 // setLayout() 方法用于设置容器的布局,
36 // BoxLayout 是一种布局管理器,它将组件以水平或垂直的方式排列。
37 //将容器的布局设置为垂直方向的
38 // BoxLayout.getContentPane() 返回容器中的内容面板,用于容纳实际的组件。
39 // Boxlayout.Y_AXIS 表示垂直方向排列组件。
40 //设置为垂直方向的 BoxLayout 后,组件将会从上到下依次排列。
41 setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
42
43 // 添加组件到窗口中
44 add(usernameLabel);//用户名标签
45 add(usernameField);//用户名文本框
46 add(passwordLabel);//密码标签
47 add(passwordField);//密码文本框
48 add(captchaLabel);//验证码标签
49 add(captchaField);//验证码文本框
50 add(captchaPanel);//验证码面板
51 add(loginButton);//登录按钮
52 add(registerButton);//注册按钮
53
54 // 设置登录按钮的点击事件
55 loginButton.addActionListener(new ActionListener() {//匿名内部类
56 public void actionPerformed(ActionEvent e) {
57 String username = usernameField.getText();
58 String password = new String(passwordField.getPassword());
59 String captcha = captchaField.getText();
60
61 // 验证输入的验证码
62 if (captcha.equalsIgnoreCase(captchaPanel.getCaptcha())) {//equalsIgnoreCase方法忽略大小写,将输入的验证码和屏幕的验证码比较
63
64
65 // 示例逻辑:判断输入是否符合要求
66 if (username.isEmpty() || password.isEmpty()) {//判断是否为空
67 //通过JOptionPane可以非常方便地创建一些简单的对话框,Swing已经为这些对话框添加了相应的组件,无须程序员手动添加组件。
68 //showMessageDialog 消息对话框,告知用户某事已发生,用户只能单击确定按钮
69 JOptionPane.showMessageDialog(LoginFrame.this, "请输入完整的登录信息");
70
71 } else {
72 JOptionPane.showMessageDialog(LoginFrame.this, "登录成功");
73 }
74 } else {
75 JOptionPane.showMessageDialog(LoginFrame.this, "验证码错误");//验证码不对输出错误
76 }
77 // 更新验证码
78 captchaPanel.generateCaptcha();
79 captchaField.setText("");
80 }
81 });
82
83 // 设置注册按钮的点击事件
84 registerButton.addActionListener(new ActionListener() {
85 public void actionPerformed(ActionEvent e) {
86 JOptionPane.showMessageDialog(LoginFrame.this, "注册成功");
87 }
88 });
89
90 // 设置窗口可见性、大小和关闭操作
91 pack();//调整窗口的大小,使其适应组件的大小和布局。
92 // 如果该窗口或其所有者仍不可显示,则两者在计算首选大小之前变得可显示。
93 // 在计算首选大小之后,将会验证该Window。窗口自动适应大小,使窗口能正好显示里面所有的控件。
94 setVisible(true);//设置可见性为true
95 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗口关闭,程序终止
96 }
97
98 // 自定义验证码面板
99 class CaptchaPanel extends JPanel {
100 private static final int WIDTH = 100;//宽度
101 private static final int HEIGHT = 30;//高度
102 private static final int FONT_SIZE = 18;//字体大小
103
104 private String captcha;//验证码
105
106 public CaptchaPanel() {
107 generateCaptcha();
108 }
109
110 // 生成随机验证码
111 public void generateCaptcha() {
112 StringBuilder sb = new StringBuilder();//StringBuilder看作是容器,创建之后内容可变,便于操作字符串
113 Random random = new Random();
114 String english="ABCDEFGHIKLMNOPQRSTUVWXYZ";
115 for (int i = 0; i < 4; i++) {
116
117 sb.append(english.charAt(random.nextInt(26)));//生成随机英文验证码
118 }
119 captcha = sb.toString();//转字符串
120 repaint();//组件重新绘制
121 }
122
123 // 获取验证码
124 public String getCaptcha() {
125 return captcha;
126 }
127 /*在自定义的组件中重写 paintComponent() 方法的示例。
128 paintComponent() 方法是一个用于绘制组件外观的方法,在 Swing 和 AWT 中常被使用。
129
130 在这段代码中,paintComponent() 方法被重写,并且通过调用父类的 super.paintComponent(g) 来确保使用默认的绘制行为。
131
132 接下来,通过 Graphics 对象 g 进行绘制操作。
133 首先使用 setColor() 方法将绘图颜色设置为黑色(Color.BLACK)。
134 然后使用 setFont() 方法设置绘制文本的字体,通过 Font.MONOSPACED 指定字体名称,Font.BOLD 指定字体样式为粗体,FONT_SIZE 指定字体大小。
135
136 最后,使用 drawString() 方法绘制验证码文本。
137 drawString() 方法用于在指定位置绘制字符串,它接受四个参数:绘制的文本字符串、起始位置的 x 坐标、起始位置的 y 坐标。
138 在这段代码中,captcha 是要绘制的验证码文本,WIDTH / 2 - 2 * FONT_SIZE 和 HEIGHT / 2 + FONT_SIZE / 2 是计算得到的绘制文本的起始位置。
139 具体的计算方式根据实际需求而定。
140 */
141 @Override
142 protected void paintComponent(Graphics g) {
143 super.paintComponent(g);
144 g.setColor(Color.BLACK);
145 g.setFont(new Font(Font.MONOSPACED, Font.BOLD, FONT_SIZE));
146 g.drawString(captcha, WIDTH / 2 - 2 * FONT_SIZE, HEIGHT / 2 + FONT_SIZE / 2);
147 }
148 /*
149 重写了 getPreferredSize() 方法,用于指定组件的首选大小。
150
151 在 Swing 和 AWT 中,组件的首选大小是一个重要的概念,它表示了组件在没有被其他布局管理器或父容器指定大小限制的情况下,希望显示的大小。
152
153 在这段代码中,getPreferredSize() 方法被重写,并且返回了一个 java.awt.Dimension 对象,该对象封装了组件的宽度(WIDTH)和高度(HEIGHT)。
154
155 通过返回一个包含所需尺寸的 Dimension 对象,可以告诉布局管理器和父容器组件应该有多大空间来显示该组件。
156
157 通常情况下,父容器和布局管理器会根据组件的首选大小来决定如何布局和调整组件的大小。但需要注意的是,父容器和布局管理器可以选择忽略首选大小,根据实际需求进行调整。
158
159 因此,getPreferredSize() 方法返回的大小并不是组件最终显示的大小的唯一决定因素,但它提供了一个指导给父容器和布局管理器的建议值。
160
161 通过重写 getPreferredSize() 方法,我们可以定义一个自定义组件的首选大小,以便在容器中正确布局和显示它。
162 */
163 @Override
164 public java.awt.Dimension getPreferredSize() {
165 return new java.awt.Dimension(WIDTH, HEIGHT);
166 }
167 }
168
169 public static void main(String[] args) {
170 /*SwingUtilities.invokeLater()方法接受一个Runnable实例作为参数,该实例定义了要在EDT中执行的代码块。
171 在这段代码中,使用了匿名内部类创建了一个Runnable实例,重写了run()方法,在run()方法中创建了一个LoginFrame对象。
172 该代码的目的是在EDT中创建一个LoginFrame对象并显示出来。
173 通过使用invokeLater()方法,可以确保在正确的线程中执行UI相关的操作,以避免可能的线程安全问题。
174 总结起来,这段代码通过SwingUtilities.invokeLater()方法将创建和显示LoginFrame对象的操作推迟到EDT中执行,确保了Swing组件在安全的线程上进行操作。
175 */
176 SwingUtilities.invokeLater(new Runnable() {
177 public void run() {
178 new LoginFrame();
179 }
180 });
181 }
182 }