Java Swing图形化用户界面编程

GUI概述

早期计算机系统中,电脑向用户提供的是单调、枯燥、纯字符状态的“命令行界面(CLI)”。就是到现在,我们还可以依稀看到它们的身影:在Windows中开个DOS窗口,就可看到历史的足迹。

后来,Apple公司率先在电脑的操作系统中实现了图形化的用户界面(Graphical User Interface,简称GUI),但由于Apple公司封闭的市场策略,自己完成电脑硬件、操作系统、应用软件一条龙的产品,与其它PC不兼容。这使得Apple公司错过了一次一统全球PC的好机会。

后来,著名的Microsoft公司推出了风靡全球的Windows操作系统,它凭借着优秀的图形化用户界面,一举奠定了操作系统标准的地位。这也造就了世界首富---比尔.盖茨和IT业的泰山北斗微软公司。

在图形用户界面风行于世的今天,一个应用软件没有良好的GUI是无法让用户接受的。而Java语言也深知这一点的重要性,它提供了一套可以轻松构建GUI的工具。

在Java语言提供的GUI构建工具中,可以分为“组件”(component)和“容器”(container)两类。

在Java语言中,提供了以下组件:

  • 按钮
  • 标签
  • 复选框
  • 单选按钮
  • 选择框
  • 列表框
  • 文本框
  • 滚动条
  • 画布
  • 菜单

这些组件,我们在使用Windows操作系统时都遇到过,我们能够通过操作这引起组件来实现与程序的交互。

而光有“组件”是不能组装成程序的,我们必须使用“容器”将这些“组件”装配起来,使其成为一个整体。Java语言还提供了以下“容器”:

  • 窗体(Form)
  • 对话框(Dialog)

Java语言是通过AWT(抽象窗口化工具包)和Java基础类(JFC或更常用的Swing)来提供这些GUI组件的。

其中Java.awt是最原始的GUI工具包,存放在java.awt包中。现在有许多功能被已被Swing取代并得到了很大的增加与提高,因此一般我们很少再使用Java.awt,但是AWT中还是包含了最核心的功能,通常,一个Java的GUI程序至少还要使用下面几个类:

  • java.awt.Color:基本颜色定义
  • java.awt.Font:基本字体定义
  • java.awt.Cursor:光标操作定义

而Swing是JAVA提供的一组丰富的与平台无关的方式来创建图形用户界面的库。在这篇文章中,我们将学习Swing GUI控件。

Swing概述

Swing API 可扩展 GUI组件,以减轻开发者的生活创造基于JAVA前端/GUI应用。它是建立在AWT API之上,并作为 AWT API 的更换,因为它几乎每一个控制对应 AWT控制。

Swing 组件遵循MVC(模型-视图-控制器)架构,MVC让负责显示的代码、处理数据的代码、对交互进行响应并驱动变化的代码彼此分离。

MVC

假设我们要举办一场时装秀,如果只是一个人来完成,那么这个人就身兼数职:设计、修改时装,到T台展示时装。

那么如果我们使用MVC模式来模拟一下,就不会让一个人来做所有的事情。我们会让时装模特儿展示服装,他们扮演的角色就是视图。模特知道展示服装(数据)的适当方法,但是他们不知道如何设计和制作服装。我们让时装设计师充当控制器,时装设计师对于如何在 T 台上走秀没有概念,但他能设计和制作服装。时装模特和设计师都能独立地处理服装,但都有自己的专业领域。这就是MVC设计模式背后的概念:“把角色分开”

Swing特点

  • 重量轻-Swing组件是独立的原生操作系统的API与Swing API控件呈现大多采用纯JAVA代码,而不是底层的操作系统调用。

  • 丰富的控件-Swing提供了一套丰富的先进的控制系统,如树,JTabbedPane,滑块,颜色选择器,表格控件

  • 高度可定制-Swing控件可以定制视觉外观是非常简单的方法,独立的内部表示。

  • 可插拔的外观和感觉-基于Swing GUI应用程序外观和风格基于可用值,可以在运行时改变。

组件与容器

JComponent

Swing的整个可视组件库的基础构造块是JComponent,它是所有组件的父类,并且是一个抽象类,所以我们不能直接创建JComponent对象,但是作为类层次结构的基础。从API中我们看到它包含了上百个方法,Swing中的每个组件都可能使用这些方法。

组件都是矩形形状,组件本身有一个默认的坐标系,组件的左上角的坐标值是(0,0)。如果一个组件的宽是20,高是10,那么,该坐标系中,x坐标的最大值是20;y坐标的最大值是10。

组件默认的边框是一个黑边的矩形,我们可以通过方法

public void setBorder(Border border)
设置组件的边框。该方法的参数是一个接口,必须向该参数传递一个实现接口Border类的实例。如果传递一个null,组件将取消边框。

组件的颜色:

  • public void setBackground(Color c):设置组件的背景色。
  • public void setForeground(Color c):设置组件的前景色。
  • public Color getBackground(Color c):获取组件的背景色。
  • public Color getForeground(Color c):获取组件的前景色。
  • public Color(int red,int green,ing blue):创建一个颜色对象,其中red、green、blue的取值在0到255之间。

组件的字体:

  • public void setFont(Font f):组件调用该方法设置组件上的字体。
  • public Font getFont(Font f):组件调用该方法获取组件上的字体。

上述方法中用到了java.awt包中的Font类,Font类的构造方法是:

public Font(String name,int style,int size) // 创建字体对象。name是字体的名字,style决定字体的样式,取值是一个整数。

组件的大小与位置:

  • public void setSize(int width,int height):设置组件的大小。
  • public void setLocation(int x,int y):设置组件在容器中的位置,组件距容器的左、上边界 x、y 个像素。
  • public Dimension getSize():返回一个Dimension对象的引用,该对象实体中当前组件的宽度和高度。
  • public Point getLocation(int x,int y):返回一个Point对象的引用,该对象实体中含有组件的左上角在容器的坐标系中的x坐标和y坐标。
  • public void setBounds(int x,int y,int width,int height):设置组件在容器中的位置和组件的大小。
  • public Rectangle getBounds():返回一个Rectangle对象的引用,该对象实体中含有当前组件左上角在容器坐标系中的x坐标和y坐标,宽度和高度。

组件默认是不透明的,我们可以通过方法

public void setOpaque(boolean isOpaque)
设置组件是否不透明。isOpaque取false时,组件被设置为透明;isOpaque取true时组件被设置为不透明。方法
public boolean isOpaque()
当组件不透明时该方法返回true,否则返回false。

组件的激活与可见性:

  • public void setEnabled(boolean b):设置组件是否可被激活。
    当参数b取值true时,组件可以被激活。
    当参数b取值false 时,组件不可激活。
    默认情况下,组件是可以被激活的。
  • public void setVisible(boolean b):设置组件在该容器中的可见性。
    当b取值true时,组件在容器中可见。
    当b取值false时,组件在容器中不可见。
    除了Window型组件外,其它类型组件默认是可见的。

我们再来认识几个方法:

  • add( ):将组件添加到该容器中。
  • removeAll() :移掉容器中的全部组件。
  • remove(Component c) 移掉容器中参数指定的组件。
  • validate():每当容器添加新的组件或移掉组件时,调用该方法以保证容器中的组件能正确显示出来。

JFrame

JFrame类是一个顶层窗口,也是一个容器,允许将其他组件添加到它里面,把它们组织起来,并把它们呈现给用户。它有许多其他好处,我们先看最简单的图片:

JFrame实际上不仅仅让您把组件放入其中并呈现给用户。比起它表面上的简单性,它实际上是 Swing 包中最复杂的组件。为了最大程度地简化组件,在独立于操作系统的Swing组件与实际运行这些组件的操作系统之间,JFrame起着桥梁的作用。JFrame在本机操作系统中是以窗口的形式注册的,这么做之后,就可以得到许多熟悉的操作系统窗口的特性:最小化/最大化、改变大小、移动。

常用构造方法:

  • JFrame():可以创建一个无标题的窗口。
  • JFrame(String title):创建一个标题为title的窗口

常用方法:

  • getTitle()/setTitle():获取/设置窗口的标题。
  • getState/setState():获取/设置窗口的最小化、最大化等状态。
  • isVisible()/setVisible():获取/设置窗口的可视状态(即是否在屏幕上显示)。
  • getLocation/setLocation():获取/设置窗口在屏幕上应当出现的位置。
  • getSize()/setSize():获取/设置窗口的大小。
  • setBounds():获取/设置窗口出现在屏幕上时的初始位置与大小。
  • setResizable():设置窗口是否可调整大小,窗口默认是可调整大小的。
  • add():将组件添加到窗口中。
  • setDefaultCloseOperation() :设置单击窗体右上角的关闭图标后,程序会做出怎样的处理。

JDialog

对话框类。

JDialog类是Window的子类。对话框必须要依赖于某个窗口或组件,当它所依赖的窗口或组件消失,对话框也将消失;当它所依赖的窗口或组件可见时,对话框又会自动恢复。

我们通过建立JDialog的子类来建立一个对话框类。不可以把组件直接添加到对话框中,不能为对话框设置布局,可以使用getContentPane()方法得到内容面板。

对话框可分为无模式和有模式两种。

如果一个对话框是有模式的对话框,那么当这个对话框处于激活状态时,只让程序响应对话框内部的事件,程序不能再激活它所依赖的窗口或组件,而且它将堵塞当前线程的执行,直到该对话框消失不可见。

无模式对话框处于激活状态时,程序仍能激活它所依赖的窗口或组件,它也不堵塞线程的执行。

消息对话框:

消息对话框是有模式对话框。进行一个重要的操作动作之前,最好能弹出一个消息对话框以确定操作。

可以用javax.swing包中的JOptionPane类的静态方法:

public static void showMessageDialog (Component parentComponent,String message,String title, int messageType)

创建一个消息对话框。参数分别是对话框所依赖的组件、对话框上显示的消息,对话框的标题,对话框的外观。

确认对话框:

确认对话框是有模式对话框。可以用javax.swing包中的JOptionPane类的静态方法:

public static int showConfirmDialog (Component parentComponent,Object message,String title,int optionType)

创建一个确认对话框。

颜色对话框:

可以用javax.swing包中的JColorChooser类的静态方法:

public static Color showDialog (Component component,String title,Color initialColor)

创建一个颜色对话框。参数component指定对话框所依赖的组件,title指定对话框的标题;initialColor指定对话框返回的初始颜色。

文件对话框:

文件对话框提供从文件系统中进行文件选择的界面。JFileChooser对象调用下列方法:

  • showDialog(Component parent,String s)
  • showOpenDialog(Component parent)
  • showSaveDialog(Component parent)

以上方法都可以使得一个有模式对话框显示在桌面上,该对话框称作文件对话框。文件对话框将在参数指定的组件parent的正前方显示,如果parent为null,则在系统桌面的正前方显示。

菜单组件

Swing菜单由菜单条(JMenuBar)、菜单(JMenu)和菜单项(JMenuItem)构成。菜单条是所有菜单和菜单项的根(容器)。

JMenuBar

JMenuBar是JComponent类的子类,负责创建菜单条。

JMenuBar menuBar=new JMenuBar();

将菜单条放置到JFrame窗口中:

public void setJMenuBar(JMenuBar menubar);

需要注意的是,只能向窗口添加一个菜单条。

JMenu

JMenu是JComponent类的间接子类,负责创建菜单。

构造方法:

  • pulbic JMenu():建立一个没有标题的菜单。
  • pulbic JMenu(String title):建立一个指定标题菜单,标题由参数title确定

常用方法:

  • add(MenuItem item):向菜单增加由参数item指定的菜单选项对象。
  • add(String s):向菜单增加指定的选项。
  • getItem(int n):得到指定索引处的菜单选项。
  • getItemCount():得到菜单选项数目。

创建一个名为“Edit”的菜单,并添加到菜单条中:

JMenu editMenu=new JMenu(“Edit”);
menuBar.add(editMenu);

JMenuItem

JMenuItem是JComponent类的间接子类,负责创建菜单项。

常用构造方法:

  • public JMenuItem(String s):构造有标题的菜单项。
  • public JMenuItem(String text, Icon icon):构造有标题和图标的菜单项。
    创建图标对象 Icon icon=new ImageIcon(“图标文件名”);

常用方法:

  • public void setEnabled(boolean b):设置当前菜单项是否可被选择。
  • public String getLabel():得到菜单选项的名字。
  • public void setAccelerator(KeyStroke keystroke):为菜单项设置快捷键。
    KeyStroke类静态方法:
    public static KeyStroke getKeyStroke(int keyCode, int modifiers)

    keyCode一般取值范围:KeyEvent.VK_A~KeyEvent.VK_Z
    modifiers一般取值:InputEvent.ALT_MASK,InputEvent.CTRL_MASK,InputEvent.SHIFT_MASK

创建一个菜单项,并将其放在“Edit”菜单里:

JMenuItem pasteItem=new JMenuItem(“Paste”);
editMenu.add(pasteItem);

嵌入JMenu子菜单

菜单项本身也可以是一个菜单,我们称这样的菜单项为子菜单。如:

JMenu editMenu = new JMenu(“Edit”);
JMenu optionsMenu = new JMenu(“Options”);
editMenu.add(optionsMenu);

布局管理器

作用:

  • 指定各个视图组件在窗口中的分布位置,实现动态布局。
  • 有助于创建复合式的复杂界面,一个复合式界面由一些简单的基本界面组成。

布局管理器的类型:

  • 流布局管理器:FlowLayout
  • 边界布局管理器:BoderLayout
  • 卡片式布局管理器:CardLayout
  • 网格布局管理器:GridLayout
  • 盒式布局管理器:BoxLayout

我们使用方法 setLayout(LayoutManager) 来设计自己的布局。

BorderLayout

边界布局管理器,这是Window型容器的默认布局,比如Jframe类、JDialog类。

每个被BorderLayout管理的容器均被划分为五个区域:东(EAST),南(SOUTH),西(WEST),北(NORTH),中(CENTER)。

容器的每个区域,只能加入一个组件,如果试图加入多个组件,其中只有一个组件是可见的。

对于东南西北这四个边界区域,如果,其中的某个区域没有用,它的大小将变为0,CENTER区域将扩展并占用该区域。

在BorderLayout布局管理器的管理下,组建必须通过add()方法加入到容器的五个命名区域之一,否则它们是不可见的。方法:add(组件, 区域)

FlowLayout

流式布局管理器,将组件按照加入的顺序逐个地放在容器中的一行上,一行放满后再另起一个新行。

每一行中的组件按布局指定的方式对齐方,默认情况下是居中对齐。

FlowLayout布局管理器不强行设定组件的大小,而是允许组件拥有自己希望的大小。

每个组件都有一个getPreferredSize()方法,容器布局管理器会调用此方法取得每个组件希望的大小。

FlowLayout布局管理器是JPanle容器的默认布局管理器。

GridLayout

网格式的布局管理器,它将容器空间划分成若干行乘若干列的网格,每个格放一个组件。

各组件按照从上到下,从左至右的顺序排列。

使用GridLayout布局设计的一般步骤:

  1. 使用GridLayout的构造方法创建布局对象,指定划分网格的行数和列数。
  2. 使用GridLayout布局容器调用方发add将组件加入容器。

GridLayout布局中每个网格都是相同大小并且强制组件与网格相同大小。

CardLayout

卡式布局管理器,可以容纳多个组件,但是同一时刻容器只能从这些组件中选出一个来显示,被显示的组件占据容器的整个空间。

选项卡窗格(JTabbedPane)的默认布局是CardLayout。

BoxLayout

盒式布局管理器。Box类创建的容器称作一个盒式容器,盒式容器的的默认布局是盒式布局,而且不允许更改盒式容器的布局。

行型盒式布局,特点:

  • 容器中组件的上沿在同一水平线上。
  • Box类的类方法createHorizontalBox()可以获得一个具有行型盒式布局的盒式容器。
  • 行型盒式容器中组件间的间距:Box类调用静态方法createHorizontalStrut(int width)可以得到一个不可见的水平Struct类型对象,称做水平支撑。

列型盒式布局,特点:

  • 容器中组件的左沿在同一垂直线上。
  • Box类的类方法createVerticalBox()可以获得一个具有列型盒式布局的盒式容器。
  • 列型盒式容器中组件间的间距:Box类调用静态方法createVertialStrut(int height)可以得到一个不可见的垂直Struct类型对象,称做垂直支撑。

中间容器

用来添加组件的轻容器,称为中间容器。包括:

  • JPanel
  • JScrollPane
  • JSplitPane
  • JLayeredPane

JPanel

JPanel类用来创建一个面板对象,可以向这个面板添加组件(直接使用add方法)。

使用时需要把这个面板添加到底层容器或其他中间容器中。

JPanel面板的默认布局是FlowLayout布局。

JScrollPane

滚动窗格,把一个组件放到一个滚动窗格中,然后通过滚动条来观察这个组件。

例如,JTextArea不自带滚动条,如果希望使用带滚动条的多行文本框,可把JTextArea放到一个滚动窗格中。

JSplitPane

拆分窗格,将容器拆分成两部分,拆分窗格有两种类型:

  • 水平拆分:用一条拆分线把容器分成左右两部分,拆分线可以水平移动。
  • 垂直拆分:用一条拆分线分成上下两部分,拆分线可以垂直移动。

JLayeredPane

分层窗格,如果添加到容器中的组件经常需要处理重叠问题,就可以将组件添加到JLayeredPane容器中。

JLayeredPane将容器分成5个层,容器使用add(Jcomponent component, int layer)方法添加组件component,并指定component所在的层layer。

layer取值:

  • DEFAULT_LAYER:最底层
  • PALETTE_LAYER
  • MODAL_LAYER
  • POPUP_LAYER
  • DRAG_LAYER:最上面的层

JTextField

单行文本框,用来建立文本框的组件,用户可以在文本框中输入单行的文本。

常用构造方法:

  • JTextField(int x):创建文本框对象,可以在文本框中输入若干个字符,文本框的可见字符个数由参数x指定。

  • JTextField(String s):创建文本框对象,则文本框的初始字符串为s,可以在文本框中输入若干个字符。

常用方法:

  • public void setText(String text):设置文本框中的文本为参数text指定的文本。
  • public String getText():获取文本框中的文本。
  • public void setEditable(boolean b):指定文本框的可编辑性(默认为true-可编辑的)。
  • public void setHorizontalAlignment(int alignment):设文本在文本框中的对齐方式,其中alignment的有效值确定对齐方式,如:LEFT、CENTER、RIGHT。

JPasswordField

密码框,用于接收密码信息,输入的文本不会以明文形式显示出来。

常用方法:

  • setEchoChar(char echoChar):设置回显字符(默认的回显字符是‘*’)。
  • char[] getPassword():获取密码框中的密码。

JTextArea

多行文本框,用户可以在文本区输入多行的文本。

常用构造方法:

  • JTextArea(int rows, int columns):构造一个可见行和可见列分别是rows、columns的文本区。

常用方法:

  • setLineWrap(boolean b):决定输入的文本能否在文本区的右边界自动换行。
  • setWrapStyleWord(boolean b):决定是以单词为界(b取true时)或以字符为界(b取false时)进行换行。
  • getText():获取文本区的文本。
  • setText(String text):设置文本内容。
  • append(String text):尾部加文本。
  • insert(String text,int index):在文本区的指定位置处插入文本。
  • public void copy():拷贝选定的区域。
  • public void cut():剪切选定的区域。
  • public void paste():在指定的区域粘贴。

JButton

按钮组件,常用构造方法:

  • public JButton(String text):创建名字是text的按钮。
  • public JButton(Icon icon):创建带有图标icon的按钮。
  • public JButton(String text, Icon icon):创建名字是text且带有图标icon的按钮。

常用方法:

  • public String getText():获取当前按钮上的名字。
  • public void setText(String text):重新设置当前按钮的名字,名字由参数text指定。
  • public Icon getIcon():获取当前按钮上的图标。
  • public void setIcon(Icon icon):重新设置当前按钮上的图标。
  • public void setHorizontalTextPosition(int textPosition):设置按钮名字相对按钮上图标的水平位置。
    textPosition取值:
    SwingConstants.RIGHT
    SwingConstants.LEFT
    SwingConstants.CENTER
    SwingConstants.LEADING
    SwingConstants.TRAILING
  • public void setVerticalTextPosition(int textPosition):设置按钮上名字相对按钮上图标的垂直位置。
  • public void addActionListener(ActionListener):按钮对象调用该方法可以向按钮增加动作监视器。
  • public void removeActionListener(ActionListener):按钮对象调用该方法可以移去按钮上的动作监视器。

JLabel

标签组件,一般用来显示信息,但没有编辑功能。

常用构造方法:

  • public JLabel():创建空标签对象。
  • public JLabel(String text):创建带文本的标签对象。
  • public JLabel(String text, int aligment):创建带文本的标签对象,并设置文本对齐方式

常用方法:

  • String getText():获取标签的名字。
  • void setText(String s):设置标签的名字。

JCheckBox

复选框,提供两种状态,一种是选中,另一种是未选中,用户通过单击该组件切换状态。如果不对复选框进行初始化设置,默认的初始化设置均为未选中。

常用构造方法:

  • public JCheckBox():创建一个没有名字的复选框。
  • public JCheckBox(String text):创建一个名字是text的复选框。

常用方法:

  • public void setSelected(boolean b):设置按钮状态--是否选中
  • public boolean isSelected():如果复选框处于选中状态该方法返回true,否则返回false。

JRadioButton

单选按钮,一组单选按钮同一时刻只能有一个被选中。

当创建了若干个单选按钮后,应使用ButtonGroup再创建一个对象,然后利用这个对象把这若干个单选按钮归组。归到同一组的单选按钮每一时刻只能选一。

JComboBox

分组框,即下拉列表。用户可以在下拉列表看到第一个选项和它旁边的箭头按钮,当用户单击箭头按钮时,列表选项打开。

常用构造方法:

  • public JComboBox():创建一个没有选项下拉列表。

常用方法:

  • public void addItem(Object anObject):增加选项。
  • public int getSelectedIndex():返回当前下拉列表中被选中的选项的索引,索引的起始值是0。
  • public Object getSelectedItem():返回当前下拉列表中被选中的选项。
  • public void removeItemAt(int anIndex):从下拉列表的选项中删除索引值是anIndex 选项。
  • public void removeAllItems():删除全部选项。
  • public void addItemListener(ItemListener):向下拉列表增加ItemEvent事件的监视器。

JTable

表格对象。

常用构造方法:

public JTable(Object[][] data , Object[] columnName)

  • 表格的视图将以行和列的形式显示数组data每个单元中对象的字符串表示。
  • 参数columnName用来指定表格的列名。
  • 用户在表格单元中输入的数据都被认为是一个Object对象。
  • 用户通过表格视图对表格单元中的数据进行编辑,以达到修改二维数组data中对应的数据。

JPopupMenu

弹出式菜单,由JPopupMenu类负责创建,通常用于右键菜单。

常用构造方法:- public JPopupMenu():构造无标题弹出式菜单。- public JPopupMenu(String label):构造由参数label指定标题的弹出式菜单。

常用方法:

  • public void show(Component invoker, int x, int y):设置弹出式菜单在组件invoker上的弹出的位置。

事件处理

什么是事件驱动模型?

在讲解事件驱动模型之前,我们现在看看事件驱动模型的三大要素:

  • 事件源:能够接收外部事件的源体。
  • 监听器:能够接收事件源通知的对象。
  • 事件处理程序:用于处理事件的对象。

我们应该要理解任何基于事件驱动模型的开发技术都包含以上三大要素,不管是java还是.net技术,都有基于以上三大要素的事件驱动模型开发流程。

我们来看一个例子,如果有一天有个老大爷走在路上,一不小心被天上掉下来的花瓶砸到了,并且晕了过去。那么整个过程其实就是一个事件处理流程,而且我们可以非常方便的分析出刚才所提到的事件驱动模型中的三大要素:

  1. 被砸晕的这个人其实就是事件源,因为他是能够接受到外部的事件的源体。
  2. 监听器就是这个人的大脑神经,因为它会感知到疼痛。
  3. 事件处理就是这个人晕死了过去。

由于事件驱动模型在我们日常生活中是无处不在的,因此Java和其他的编程语言都将这一过程运用到了可视化编程中了。

Java事件驱动模型

假设用户单击了一个按钮,其实该按钮就是这个事件的源(可以引发事件的物体)。所有的Java Swing对象都有感知自己被操作的能力,因此JButton按钮也具有这样能力。一个事件通常必须有一个源对象,这里就是JButton对象。当单击按钮时,JButton组件类会生成一个用于存放该事件参数的ActionEvent的对象,该对象包含了事件及事件源的信息。

Java Swing图形化用户界面编程_第1张图片

当JButton感知到自己被点击以后会将这种感觉传递给某个监听器对象,该监听器对象原先已被告知对该类事件感兴趣,监听器对象仅是一种监听特定事件的对象。这里的“将事件传递给监听器”仅意味着事件源调用监听器对象中的一个特定方法,并以事件对象作为实参。监听器对象可以监听一个特定对象的事件(比如一个按钮)。

我们可以使任何类的对象成为监听器对象,只要该类实现监听器接口。在Java API中提供了各种各样的监听器接口,以满足不同类型事件的需要。在单击按钮的例子中,需要实现ActionListener接口以便接收按钮事件。在监听器接口声明的方法中,实现了接受这个事件对象并响应该事件的代码。每种监听器接口都定义了特定的方法,用来接收该监听器计划要处理的事件。

仅仅实现监听器接口还不足以将监听器对象连接到事件源上,仍需要把监听器与希望处理的事件单个源或多个源连接起来,这就是监听器的注册。通过调用事件源对象的特定方法,可以注册带有事件源的监听器对象。例如,为了注册监听单击按钮事件的监听器,需要调用JButton对象的addActionListener()方法,该操作可以使监听对象和事件源绑定。

每个事件响应时只涉及到对该事件感兴趣的监听器。由于监听器只要求实现一个合适的接口,所以实际上,可以在任何希望的地方接收和处理事件。在Java中使用监听器对象处理事件的方式,称为委托事件模型,这是因为对于诸如按钮这种组件引起的事件响应,并不是由引起事件的对象本身处理,而是委托独立的监听器对象进行处理。如ActionListener中的actionPerformed()方法其实就是一个委托处理方法。

窗口事件

WindowListener接口

JFrame类是Window类的子类,Window型对象都能触发WindowEvent事件。当一个JFrame窗口被激活、撤消激活、打开、关闭、图标化或撤消图标化时,就引发了窗口事件。通过调用 addWindowlistener()来注册监听器。

WindowListener接口中有7个不同的方法,分别是:

  • public void windowActivated(WindowEvent e):当窗口从非激活状态到激活时,窗口的监视器调用该方法。
  • public void windowDeactivated(WindowEvent e):当窗口激活状态到非激活状态时调用该方法。
  • public void windowClosing(WindowEvent e):当窗口正在被关闭时调用该方法。
  • public void windowClosed(WindowEvent e):当窗口关闭时调用该方法。
  • public void windowIconified(WindowEvent e):当窗口图标化时调用该方法。
  • public void windowDeiconified(WindowEvent e):当窗口撤消图标化时调用该方法。
  • public void windowOpened(WindowEvent e):当窗口打开时调用该方法。

鼠标事件

鼠标事件是鼠标的移动、点击、拖放等行为。鼠标在组件上的操作(如点击按钮、菜单),不需要处理为鼠标事件,该组件会进行相应的处理。

用户的下列7种操作都可以使得组件触发鼠标事件:

  • 鼠标指针从组件之外进入
  • 鼠标指针从组件内退出
  • 鼠标指针停留在组件上时,按下鼠标
  • 鼠标指针停留在组件上时,释放鼠标
  • 鼠标指针停留在组件上时,单击鼠标
  • 在组件上拖动鼠标指针
  • 在组件上运动鼠标指针

MouseListener接口与MouseMotionListener接口

MouseListener、MouseMotionListener都是处理MouseEvent的接口。

MouseListener处理对象对于鼠标的进入、离开、下压、释放及敲击事件,使用addMouseMotionListener (MouseListener listener) 注册监听器。

MouseListener接口中的5个方法:

  • mousePressed(MouseEvent):负责处理鼠标按下触发的鼠标事件。
  • mouseReleased(MouseEvent e):负责处理鼠标释放触发的鼠标事件。
  • mouseEntered(MouseEvent e):负责处理鼠标进入组件触发的鼠标事件。
  • mouseExited(MouseEvent e):负责处理鼠标退出组件触发的鼠标事件。
  • mouseClicked(MouseEvent e):负责处理鼠标单击或连击触发的鼠标事件。

MouseMotionListener接口

MouseMotionListener用于处理鼠标的移动及拖曳,使用addMouseMotionListener(MouseListener listener)方法注册监听器

MouseMotionListener接口中的两个方法:

  • mouseDragged(MouseEvent e):负责处理鼠标拖动事件
  • mouseMoved(MouseEvent e):负责处理鼠标移动事件

MouseEvent类

MouseEvent类的重要方法:

  • getX():返回触发当前鼠标事件时,鼠标指针在事件源坐标系中的x-坐标。
  • getY():返回触发当前鼠标事件时,鼠标指针在事件源坐标系中的y-坐标。
  • getClickCount():返回鼠标被连续单击的次数。
  • getModifiers():返回一个整数值。左键返回的值是常量BUTTON1_MASK,右键返回的值是常量BUTTON3_MASK。
  • getSource():返回触发当前鼠标事件的事件源。

鼠标位置的坐标变换

程序可能需要知道鼠标指针在容器坐标系中的坐标,这就需要进行坐标变换。

  • public static Point convertPoint(Component source, int x, int y,Component destination):该方法返回一个Point对象。

根据鼠标指针在当前事件源source坐标系中的坐标(x,y),得到鼠标在容器 destination坐标系中的坐标。该对象再调用getX()和getY()方法就可以获取鼠标在容器destination坐标系中的坐标。

鼠标事件的转移

假如正监视一个容器上的鼠标事件,而容器中添加了一些组件,则当在组件上鼠标操作时,容器将不知道这些操作的发生。

  • MouseEvent convertMouseEvent(Component source, MouseEvent sourceEvent, Component destination):使鼠标事件从一个事件源转移到另一个事件源上。该方法是javax.swing包中的SwingUtilities类的静态方法。

获取鼠标在系统桌面上的坐标

PointerInfo类

是JDK1.5在java.awt包中新增的一个类,可以帮助程序获取鼠标指针在系统图形设备中的位置坐标。使用MouseInfo的方法:getPointerInfo(),可以实例化一个PointerInfo对象。

方法:

  • GraphicsDevice getDevice():返回在创建此 PointerInfo 时鼠标指针所在的 GraphicsDevice。
  • Point getLocation():返回在屏幕上表示指针坐标的 Point。

键盘事件

在具有键盘焦点的组件中按下或者释放按键时,将会激发键盘事件。键盘事件由接口KeyListener的方法来处理,组件使用addKeyListener()注册监听器。

接口KeyListener中有3个方法:

  • public void keyPressed(KeyEvent e):按下键盘上某个键时,方法keyPressed方法会自动执行。
  • public void keyTyped(KeyEvent e):当键被按下又释放时,keyTyped方法被调用
  • public void KeyReleased(KeyEvent e):当键被释放时,KeyReleased方法被调用。

KeyEvent

KeyEvent的重要方法:

  • public int getKeyCode():返回一个整形值,常用于在keyPressed方法中获取用户敲击的键的虚拟键代码。
  • public char getKeyChar():返回与此事件中的键关联的字符。
  • public int getModifiers():返回此事件的修饰符掩码。以指出所按住的修改键(例如 shift、ctrl、alt、meta),该动作被定义为 InputEvent 的一部分。

java对字符和虚拟键代码进行了明确区分,虚拟键代码和键盘的扫描码类似,没有单独的小写虚拟键代码。虚拟键代码以VK_开头,是定义在KeyEvent类中的int型类变量,比如:VK_0, VK_A, VK_SHIFT, VK_RIGHT。在使用keyPressed()和keyReleased()方法时,需要进行虚拟键代码的检查,在使用keyTyped()方法时需要进行字符进行检查

示例

使用GUI实现简易计算器功能。

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
 * 计算器类
 * 
 * @author 小明
 *
 */
public class Calculator extends JFrame implements ActionListener {

	private static final long serialVersionUID = 2499964079704437558L;

	private JTextField result; // 显示运算结果的文本框
	private JButton[] buttons; // 所有的按钮对象
	private final String[] characters = { "7", "8", "9", "/", "4", "5", "6",
			"*", "1", "2", "3", "-", "0", ".", "=", "+" }; // 所有的按钮文本
	private boolean isFirstDigit = true; // 标记第一次输入数字
	private String operator = "="; // 运算符
	private double resultNum = 0.0; // 运算结果

	public Calculator(String title) {
		// 标题栏
		super(title);
		// 大小
		setSize(220, 200);
		// 居中
		setLocationRelativeTo(null);
		// 默认关闭操作
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		// 禁止修改大小
		setResizable(false);
		// 初始化文本框与按钮
		generateInterface();
		// 显示
		setVisible(true);
	}

	/**
	 * 初始化文本框与按钮,生成界面
	 */
	private void generateInterface() {
		/* 文本框 */
		result = new JTextField("0");
		// 右对齐
		result.setHorizontalAlignment(JTextField.RIGHT);
		// 不允许编辑
		result.setEditable(false);
		// 将文本框添加到窗体北方
		add(result, BorderLayout.NORTH);

		/* 按钮 */
		buttons = new JButton[characters.length];
		JPanel pnl = new JPanel(new GridLayout(4, 4, 5, 5));
		for (int i = 0; i < buttons.length; i++) {
			buttons[i] = new JButton(characters[i]);
			buttons[i].addActionListener(this);
			buttons[i].setFocusable(false); // 不允许按钮定位焦点
			pnl.add(buttons[i]);
		}
		// 将所有按钮添加到窗体的中间
		add(pnl, BorderLayout.CENTER);

		// 允许内容面板定位焦点
		this.getContentPane().setFocusable(true);

		// 注册内容面板事件监听器
		// 使用适配器实现
		this.getContentPane().addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				char ch = e.getKeyChar(); // 获取按钮字符
				/* 处理数字或运算符 */
				if ('.' == ch || Character.isDigit(ch)) { // 数字小数点
					handleNumber(String.valueOf(ch));
				} else if ("+-*/=".indexOf(ch) != -1 || e.getKeyCode() == 10) { // 运算符
					handleOperator(String.valueOf(ch));
				} else if (e.getKeyCode() == 8) { // 退格键
					String tmp = result.getText();
					if (tmp.length() == 1) {
						result.setText("0");
						isFirstDigit = true;
					} else {
						result.setText(tmp.substring(0, tmp.length() - 1));
					}
				}
			}
		});
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// 获取点击按钮的文本
		String text = e.getActionCommand();

		/* 处理数字或运算符 */
		if (".".equals(text) || Character.isDigit(text.charAt(0))) { // 数字小数点
			handleNumber(text);
		} else if ("+-*/=".indexOf(text) != -1) { // 运算符
			handleOperator(text);
		}
	}

	/**
	 * 处理数字与小数点
	 * 
	 * @param text
	 */
	private void handleNumber(String text) {
		if (isFirstDigit) { // 第一次输入
			if (".".equals(text)) {
				this.result.setText("0.");
			} else {
				this.result.setText(text);
			}
		} else if ("0".equals(text) && "0".equals(this.result.getText())) { // 输入0
			isFirstDigit = true;
			return;
		} else if (".".equals(text) && this.result.getText().indexOf(".") == -1) { // 输入小数点
			this.result.setText(this.result.getText() + ".");
		} else if (!".".equals(text)) { // 输入不为小数点
			this.result.setText(this.result.getText() + text);
		}
		// 修改第一次输入标记
		isFirstDigit = false;
	}

	/**
	 * 处理运算符
	 * 
	 * @param text
	 */
	private void handleOperator(String text) {
		/* 进行算术运算判断 */
		switch (operator) {
		case "+":
			resultNum += Double.parseDouble(this.result.getText());
			break;
		case "-":
			resultNum -= Double.parseDouble(this.result.getText());
			break;
		case "*":
			resultNum *= Double.parseDouble(this.result.getText());
			break;
		case "/":
			resultNum /= Double.parseDouble(this.result.getText());
			break;
		case "=":
			resultNum = Double.parseDouble(this.result.getText());
			break;
		}
		// 将运算结果显示到文本框中
		this.result.setText(String.valueOf(resultNum));
		// 将参数运算符放入成员变量中
		this.operator = text;
		// 下一个数字是第一次输入
		isFirstDigit = true;
	}
}

结果


你可能感兴趣的:(Java程序语言)