目录
1.Java中的GUI
(1)AWT
(2)Swing
2.swing中常用容器和组件
(1)常用容器
(2)常用组件
3.布局管理器
(1)绝对布局(空布局)
(2)流布局管理器FlowLayout
(3)边框布局管理器BorderLayout
(3)网格布局管理器GridLayout
4.事件处理
(1)事件处理的原理
(2)常用监听器
5.监听器的多种实现方式
(1)匿名内部类实现
(2)普通内部类的实现
(3)窗体实现监听器类接口
(4)外部类实现
GUI(Graphical User Interface):图形用户界面
Java的GUI程序设计技术主要包括AWT、Swing和SWT(使用SWT需要从网上下载安装额外的Java包)
AWT的类是使用原始的GUI对象来运行,在不同的操作系统上,底层对于界面的显示支持不同,导致AWT的程序在不同操作系统上不兼容
Swing包中提供的类加强了对各种操作系统的兼容性,在Java中,对于不同的操作系统,这些类可以更加充分地发挥作用,Swing类支持许多AWT中相似的类所不能支持的特性,但Swing并没有完全替代AWT
(1)AWT
java.awt包是Java内置的包,属于Java基本类库(JFO)的一部分
import java.awt.*
(2)Swing
- 轻量级组件
- 可插入外观组件(并且外观不随操作系统而发生变化)
Swing是在AWT基础上发展而来的轻量级组件(AWT时),与AWT相比不但改进了用户界面,而且所需的系统资源更少
Swing是纯Java组件,完全由Java编写,使所有的应用程序在不同的平台上运行时具有本机外观和相同的行为
Swing不仅包括了AWT所具有的全部组件,而且可以使用树形组件(JTree)、表格(JTable)、选项卡(JTabbedPane)等高级图形组件
事实上,AWT和Swing技术极为相似,GUI组件的类名通常只是比AWT组件的类名多了一个字J
想在窗体上添加组件,我们就应该清楚是要在窗体的哪一部分添加组件,而窗体本身的区域就是容器(Container类型),我们可以通过获取方法获得Container对象,然后可以创建组件,通过add方法将组件添加到容器中,而这个组件添加进去在容器中怎么放置就是“布局”
布局管理器:能够决定组件或其他窗体中的排列规则的对象
使用绝对布局的窗口通常都是固定大小的,组件的位置和形状不会随着窗体的改变而发生变化。
import java.awt.Container;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java网格布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
空布局优缺点:
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java窗体示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//为窗体容器设置布局
container.setLayout(new FlowLayout(FlowLayout.LEADING,100,30));
container.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
private JButton button3;
private JButton button4;
private JButton button5;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java边框布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
button3 = new JButton("按钮3");
button4 = new JButton("按钮4");
button5 = new JButton("按钮5");
//获取Frame界面容器
Container container = this.getContentPane();
container.setLayout(new BorderLayout());
//向Frame的容器中加入按钮组件
container.add(button1,BorderLayout.EAST);
container.add(button2,BorderLayout.SOUTH);
container.add(button3,BorderLayout.CENTER);
container.add(button4,BorderLayout.NORTH);
container.add(button5,BorderLayout.WEST);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
通过修改BorderLayout的构造方法的参数,可以为组件之间设置间距
注意:
布局方式:把整个窗体看成是一个N行M列的二维网格阵列,窗体上的每个组件都占据一个网格,网格的大小均匀
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
private JButton button3;
private JButton button4;
private JButton button5;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java网格布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
button3 = new JButton("按钮3");
button4 = new JButton("按钮4");
//获取Frame界面容器
Container container = this.getContentPane();
container.setLayout(new GridLayout(2,2));
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
container.add(button3);
container.add(button4);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
当划分的网格数量与实际添加到窗体的组件数量不一致时:
(1)划分的网格较多,实际添加到窗体的组件的数量较少
(2)实际添加到窗体的组件的数量较多,划分的网格较多
总结:
事件源:
监听器
事件源和监听器的关系
import java.awt.Container;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java网格布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
//为按钮1添加鼠标事件监听器
button1.addMouseListener(new MouseListener() { //什么监听器就是什么Listener
@Override
public void mouseReleased(MouseEvent e) { //鼠标抬起,执行此方法
System.out.println("鼠标抬起");
}
@Override
public void mousePressed(MouseEvent e) { //鼠标按下,执行此方法
System.out.println("鼠标按下");
}
@Override
public void mouseExited(MouseEvent e) { //鼠标离开组件区域,执行此方法
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) { //鼠标进入组件区域,执行此方法
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) { //鼠标点击组件,执行此方法
// TODO Auto-generated method stub
}
});
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
分析:
事件源组件(如button1)都有添加监听器的方法(如addMouseListener(),而在方法中传递的是一个监听器(如MouseListener),这个监听器监听着一些事件的发生,Java本身对于这些监听器的实现是一个接口,定义事件的抽象方法,统一接口,然后由程序,自己来实现监听器中监听事件的行为,这样也就做到了通过统一的接口引用去调用不相同的具体实现方法,从而让事件的处理有一个统一的标准,统一的模式
除过ActionListener,其他监听器,我们基本看到它的名字就知道它监听的事件类型
ActionListener:
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java网格布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
//为按钮1添加鼠标事件监听器
button1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击:actionPerformed");
}
});
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
使用ActionListener的优点:
当我们只需要为组件添加使用频率最高的那个监听事件时
一个组件是否可以添加多个监听器?
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java网格布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
//为按钮1添加2个事件监听器
button1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击:actionPerformed1");
}
});
button1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击:actionPerformed2");
}
});
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
分析:
可以为一个组件添加多个监听器,可以通过添加监听器方法中的源代码发现,添加组件时,会产生一个监听器的列表,每当添加组件,就会将它加入到列表当中,当产生事件的时候,虚拟机会把组件的该列表中的所有监听器的事件方法都执行一遍,并且后添加到列表的监听器的代码会先被执行
同一个监听器是否可以添加到多个组件中?
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("Java网格布局管理器示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
ActionListener ae = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击:actionPerformed1");
}
};
//为按钮1和2添加相同事件监听器
button1.addActionListener(ae);
button2.addActionListener(ae);
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
结论:一个监听器可以同时监听多个组件的变化,对这些组件的事件做相同的处理
委托事件模型(或授权事件模型)
为什么要研究监听器的实现方式?
监听器的实现方式:
需求:点击button1按钮的时候在button2按钮上显示button1被点击的次数
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("匿名内部类实现示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
button1.addActionListener(new ActionListener() {
int click_count=0;
@Override
public void actionPerformed(ActionEvent e) {
++click_count;
button2.setText("button1被点击了"+click_count+"次");
}
});
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
分析:
我们遇到的问题:
结论:一个匿名内部类实现代码保证了只能被用来创建一个监听器对象,被一个组件使用,并且能访问类的私有属性
此种方式的特点:
需求:窗体上有10个按钮,每个按钮被点击的时候,都把该按钮的点击次数显示到button2上(下面的示例用2个被点击的按钮做演示)
匿名内部类实现方式:
需要给每个按钮都添加监听器,需要给每个按钮都添加匿名内部类
可以发现,如果这样把10个都写完,代码非常臃肿,它们仅仅时button1和button2这样的字符串不同,我们可以对它们的共性进行提取,使用普通内部类来实现
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame{
//创建按钮的引用
private JButton button1;
private JButton button2;
private JButton button3;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("普通内部类实现示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
button3 = new JButton("按钮3");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
button3.setBounds(400, 250, 100, 100);
class ActionListenerImp implements ActionListener{
String buttonName;
int click_count=0;
public ActionListenerImp(String buttonName) {
this.buttonName = buttonName;
}
@Override
public void actionPerformed(ActionEvent e) {
++click_count;
button2.setText(buttonName+"被点击了"+click_count+"次");
}
}
button1.addActionListener(new ActionListenerImp("button1"));
button3.addActionListener(new ActionListenerImp("button3"));
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
container.add(button3);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
结论:一个普通内部类实现代码使得可以创建多个对象,通过传参进行区别,然后通过匿名对象保证监听器对象只能被一个组件使用
需求同上
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame implements ActionListener{
//创建按钮的引用
private JButton button1;
private JButton button2;
private JButton button3;
//定义一个计数器
private int button1_count;
private int button3_count;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("窗口实现监听器接口示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
button3 = new JButton("按钮3");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
button3.setBounds(400, 250, 100, 100);
//添加监听器
button1.addActionListener(this);
button3.addActionListener(this);
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
container.add(button3);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
//监听器接口的方法
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==button1)
{
++button1_count;
button2.setText("button1被点击了"+button1_count+"次");
}
else if(e.getSource()==button3)
{
++button3_count;
button2.setText("button3被点击了"+button3_count+"次");
}
}
}
这种写法的缺点:
这种写法不推荐,弊大于利
需求同上
ButtonActionListener.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
public class ButtonActionListener implements ActionListener{
//在这个方法中要操作某个窗体中的组件,我们不能为actionPerformed方法传参来操作它
//我们可以类定义一个窗体的引用,然后通过构造方法传入到监听器当中
private MyJFrame frame;
private String button_Name;
private int count;
public ButtonActionListener(MyJFrame frame, String button_Name) {
super();
this.frame = frame;
this.button_Name = button_Name;
}
@Override
public void actionPerformed(ActionEvent e) {
count++;
//我们没有办法直接访问MyJFrame中的私有属性,只能通过MyJFrame提供get方法或提升button的访问权限来访问它
frame.button2.setText(button_Name+"被点击了"+count+"次");
}
}
MyJFrame.java
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class MyJFrame extends JFrame {
//创建按钮的引用
private JButton button1;
public JButton button2;
private JButton button3;
//在构造界面的时候就进行初始化界面
public MyJFrame() {
init();
}
//初始化界面
private void init()
{
//设置窗体
this.setSize(600,400);//设置窗体大小
this.setLocationRelativeTo(null);//设置窗体居中显示
this.setTitle("外部类实现示例");//设置窗体标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置点击窗体x号时退出程序
//为按钮引用创建按钮对象
button1 = new JButton("按钮1");
button2 = new JButton("按钮2");
button3 = new JButton("按钮3");
//获取Frame界面容器
Container container = this.getContentPane();
//设置容器布局为绝对布局,即组件的摆放不受任何限制
container.setLayout(null);
//为按钮设置位置和大小
button1.setLocation(0, 0);
button1.setSize(200, 100);
button2.setBounds(100,150, 200, 100);
button3.setBounds(400, 250, 100, 100);
//添加监听器
button1.addActionListener(new ButtonActionListener(this, "button1"));
button3.addActionListener(new ButtonActionListener(this, "button3"));
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
container.add(button3);
}
public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}
优点:
缺点:
各种写法各有利弊,根据业务需求进行选择