本周主题:Java的GUI技术

目录

​本周主题:Java的GUI技术​

​一、Java中的图形用户界面--GUI​

​(1)AWT​

​(2)Swing​

​二、Swing中常用容器和组件​

​(1)常用容器​

​(2)常用组件​

​三、布局管理器​

​(1)绝对布局(空布局)​

​(2)流布局管理器FlowLayout​

​(3)边框布局管理器BorderLayout​

​(4)网格布局管理器GridLayout​

​四、事件处理​

​(1)事件处理的原理​

​(2)常用监听器​

​五、监听器的多种实现方式​

​(1)匿名内部类实现​

​(2)普通内部类的实现​

​(3)窗体实现监听器类接口​

​(4)外部类实现​


一、Java中的图形用户界面--GUI

GUI(Graphical User Interface):图形用户界面

Java第十三周作业_布局管理器

Java的GUI程序设计技术主要包括AWT、Swing和SWT(使用SWT需要从网上下载安装额外的Java包)。

AWT的类是使用原始的GUI对象来运行,在不同的操作系统上,底层对于界面的显示支持不同,导致AWT的程序在不同操作系统上不兼容。

Swing包中提供的类加强了对各种操作系统的兼容性,在Java中,对于不同的操作系统,这些类可以更加充分地发挥作用,Swing类支持许多AWT中相似的类所不能支持的特性,但Swing并没有完全替代AWT。

Java第十三周作业_布局管理器_02

AWT和Swing的关系:

(1)AWT

java.awt包是Java内置的包,属于Java基本类库(JFO)的一部分,要使用awt必须先导入awt包:

import java.awt.*;

AWT包体系结构: 

Java第十三周作业_布局管理器_03

(2)Swing

  • 轻量级组件
  • 可插入外观组件(并且外观不随操作系统而发生变化)

Swing是在AWT基础上发展而来的轻量级组件(AWT时),与AWT相比不但改进了用户界面,而且所需的系统资源更少

Swing是纯Java组件,完全由Java编写,使所有的应用程序在不同的平台上运行时具有本机外观和相同的行为

Swing不仅包括了AWT所具有的全部组件,而且可以使用树形组件(JTree)、表格(JTable)、选项卡(JTabbedPane)等高级图形组件

事实上,AWT和Swing技术极为相似,GUI组件的类名通常只是比AWT组件的类名多了一个字符J。

Java第十三周作业_监听器_04

二、Swing中常用容器和组件

(1)常用容器

  • JFrame窗体
  • JDialog对话框
  • JPanel面板

(2)常用组件

  • JButton按钮
  • JLabel标签
  • JCheckBox多选按钮
  • JRadioButton单选按钮
  • JTextField文本框
  • JPassword密码框
  • JTextArea文本域
  • JComBox下拉框
  • JList列表框
  • JOptionPane小对话框

三、布局管理器

想在窗体上添加组件,我们就应该清楚是要在窗体的哪一部分添加组件,而窗体本身的区域就是容器(Container类型),我们可以通过获取getContentPane()方法获得Container对象,然后可以创建组件,通过add方法将组件添加到容器中,而这个组件添加进去在容器中怎么放置就是“布局”。

布局管理器:能够决定组件或其他窗体中的排列规则的对象

  • 布局管理器是用来设置容器的布局方式的

(1)绝对布局(空布局)

Java第十三周作业_监听器_05

使用绝对布局的窗口通常都是固定大小的,组件的位置和形状不会随着窗体的改变而发生变化。

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(450,300);//设置窗体大小
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(5, 5);
button1.setSize(100, 35);
button2.setBounds(50,100, 100, 35);
//向Frame的容器中加入按钮组件
container.add(button1);
container.add(button2);
}

public static void main(String[] args) {
MyJFrame frame =new MyJFrame();
frame.setVisible(true);
}
}

Java第十三周作业_布局管理器_06

空布局优缺点:

  • 优点:组件摆放灵活
  • 缺点:代码量大,自适应能力差

(2)流布局管理器FlowLayout

  • 组件的排列方向:

使用此方法来设置排列方向,通过参数设置方向,此处为从右向左排列。

container.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
  • 组件的对齐方式:

默认居中对齐:container.setLayout(new FlowLayout());

左对齐:container.setLayout(new FlowLayout(FlowLayout.LEFT));

跟队头对齐(即第一个进入窗体的组件对齐):container.setLayout(new FlowLayout(FlowLayout.LEADING));

  • 组件是否换行:

组件会自动换行

  • 设置组件之间的距离
container.setLayout(new FlowLayout(FlowLayout.LEADING,100,30));

第一个参数:对齐方式

第二个参数:横向间隔距离

第三个参数:纵向间隔距离

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);
}
}

Java第十三周作业_监听器_07

(3)边框布局管理器BorderLayout

  • 布局方式:把窗体划分成东、南、西、北、中五个区域
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的构造方法的参数,可以为组件之间设置间距

Java第十三周作业_布局管理器_08

container.setLayout(new BorderLayout(5,10));

5代表横向间距为5个像素

10代表纵向间距为10个像素

Java第十三周作业_java_09

注意:

1.中间区域空缺的情况下,四周区域不会占据中间的区域

Java第十三周作业_java_10

2.四边的组件一旦有空缺,中间的组件会占据四边的空间

Java第十三周作业_java_11

3.五个区域中,每个区域只能放置一个组件

(4)网格布局管理器GridLayout

布局方式:把整个窗体看成是一个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);
}
}

Java第十三周作业_监听器_12

当划分的网格数量与实际添加到窗体的组件数量不一致时:

(1)划分的网格较多,实际添加到窗体的组件的数量较少

container.setLayout(new GridLayout(4,4));

 

Java第十三周作业_java_13

container.setLayout(new GridLayout(5,5));

 

Java第十三周作业_java_14

(2)实际添加到窗体的组件的数量较多,划分的网格较多

 

Java第十三周作业_java_15

Java第十三周作业_监听器_16

总结:

  • 所有网格大小都完全相同
  • 所有添加到窗体上的组件都会被显示出来
  • 行数不变列数变(如上组件数量少于网格数量,但是行数仍然为5,列数被虚拟机灵活地进行调整)
  • 组件按照从左上到右下的顺序排列

四、事件处理

(1)事件处理的原理

Java第十三周作业_监听器_17

事件源:

  • 可以被操作,并能产生状态变化的组件
  • 事件源可能产生多种类型的事件

监听器

  • 负责监控事件源的状态,并在产生事件后负责处理事件
  • 任何监听器只能处理特定类型的事件
  • 监听器中的处理事件的方法都被定义成抽象方法

事件源和监听器的关系

  • 事件源只有添加了监听器,监听器才在事件产生之后执行相应的处理代码
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);
}
}

鼠标按下按钮1后:

Java第十三周作业_监听器_18

鼠标松开后: 

Java第十三周作业_java_19

分析:

事件源组件(如button1)都有添加监听器的方法(如addMouseListener(),而在方法中传递的是一个监听器(如MouseListener),这个监听器监听着一些事件的发生,Java本身对于这些监听器的实现是一个接口,定义事件的抽象方法,统一接口,然后由程序,自己来实现监听器中监听事件的行为,这样也就做到了通过统一的接口引用去调用不相同的具体实现方法,从而让事件的处理有一个统一的标准,统一的模式。

(2)常用监听器

Java第十三周作业_监听器_20

除过ActionListener,其他监听器,我们基本看到它的名字就知道它监听的事件类型

ActionListener:

  • 处理每个组件发生频率最高事件的监听器,如按钮发生最频繁的是点击事件,所以button的ActionListener用来处理点击事件
  • 它当中只定义了一个抽象方法:

Java第十三周作业_java_21

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);
}
}

  

Java第十三周作业_布局管理器_22

使用ActionListener的优点:

当我们只需要为组件添加使用频率最高的那个监听事件时

  • 1.可以节省实现其他监听事件的代码
  • 2.可以使用Lambda表达式简化代码

一个组件是否可以添加多个监听器?

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);
}
}

  

Java第十三周作业_java_23

分析:

可以为一个组件添加多个监听器,可以通过添加监听器方法中的源代码发现,添加组件时,会产生一个监听器的列表,每当添加组件,就会将它加入到列表当中,当产生事件的时候,虚拟机会把组件的该列表中的所有监听器的事件方法都执行一遍,并且后添加到列表的监听器的代码会先被执行

同一个监听器是否可以添加到多个组件中?

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);
}
}

 点击按钮1和2都执行相同方法:

  

Java第十三周作业_java_24

结论:一个监听器可以同时监听多个组件的变化,对这些组件的事件做相同的处理

委托事件模型(或授权事件模型)

事件源和监听器这种关系就是委托事件模型,即事件源发生的事件,事件源本身不去处理,而授权给监听器去处理。

五、监听器的多种实现方式

为什么要研究监听器的实现方式?

  • 关系到组件的访问权限问题
  • 涉及到数据安全问题

监听器的实现方式:

  • 匿名内部类实现
  • 普通内部类实现
  • 窗体实现监听器类接口
  • 外部类实现

(1)匿名内部类实现

需求:点击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);
}
}

  

Java第十三周作业_布局管理器_25

分析:

我们遇到的问题:

  • 问题1.监听器需要修改窗体类的私有属性

解决办法:把监听器设计为“内部”类对象

  • 问题2:监听器必须不能被重复使用,即组件button1的监听器只用来监听button1,不能被用来监听其他组件

解决办法:把监听器设计为一个匿名类对象(一个匿名类只能有一个对象,没有用任何的引用去指向它,而是直接把它传递给方法)

  • 为什么不采用如下的写法?

Java第十三周作业_监听器_26

此种写法产生了一个指向监听器的引用al,引用的存在会产生通过引用重复使用监听器对象的可能性

结论:一个匿名内部类实现代码保证了只能被用来创建一个监听器对象,被一个组件使用,并且能访问类的私有属性

此种方式的特点:

  • 操作窗体组件容易
  • 监听器对象复用程度最低
  • 安全性最高
  • 代码结构复杂

(2)普通内部类的实现

需求:窗体上有10个按钮,每个按钮被点击的时候,都把该按钮的点击次数显示到button2上(下面的示例用2个被点击的按钮做演示)

匿名内部类实现方式:

需要给每个按钮都添加监听器,需要给每个按钮都添加匿名内部类

Java第十三周作业_监听器_27

可以发现,如果这样把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);
}
}

  

Java第十三周作业_java_28


  

Java第十三周作业_java_29

  • 按钮1和按钮2有各自的监听器对象,但通过向构造方法中传参的方式复用了同一段代码
  • 使用匿名类传入到添加监听器的方法中又保证了一个监听器对象不能被其他组件复用,每个组件有各自独立的监听器对象

结论:一个普通内部类实现代码使得可以创建多个对象,通过传参进行区别,然后通过匿名对象保证监听器对象只能被一个组件使用

(3)窗体实现监听器类接口

需求同上

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 + "次");
}
}
}

这种写法的缺点:

  • 1.窗体的角色过于复杂,如果需要其他监听器,还要实现多个监听器的方法
  • 2.不同事件源共用一个处理方法,如上button1和button2共用了一个处理方法
  • 3.代码臃肿,可读性极差
  • 4.代码的安全性较差,因为监听器接口的方法被定义为public,当我们实现该接口的时候不能降低访问权限,这样外部类对象也可以访问监听器的事件处理方法,非常不安全

这种写法不推荐,弊大于利

(4)外部类实现

需求同上

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);
}
}

优点:

  • 1.代码结构清晰,监听器和窗体完全独立
  • 2.监听器复用程度最高

缺点:

  • 监听器在访问组件的时候会受到组件访问度的限制
  • 我们需要提升窗体中要被监听器访问组件的权限,安全性较差

各种写法各有利弊,需根据业务需求进行选择。