第七章 图形用户界面

7.1 绘图

1.图形环境和图形对象

(1)坐标:GUI组件左上角默认为(0,0),从左上角到右下角,水平坐标x和垂直坐标y增加,坐标的单位是像素。

(2)Graphics对象:专门管理图形环境。Graphics是一个抽象类,提供了一个与平台无关的绘图接口,这样编写的程序可以符合java平台要求。各平台上实现的JAVA系统将创建一个子类,来实现绘图功能,但是这个子类对程序员来说是透明的。在执行paint方法时,系统会传递一个指向特定平台的Graphics子类的图形对象g(名字随意)。

2.颜色和字体

(1)颜色:Color类,以及Graphics类中与颜色有关的方法。

名称

描述

public final static Color GREEN

常量 绿色

public final static Color RED

常量 红色

public Color(int r, int g, int b)

通过指定RGB三色分量(0~255)创建颜色

public int getRed()

返回某颜色对象的红色分量值(0~255)

Graphics: public void setColor(Color c)

Graphics类方法,用于设置组件的颜色

Graphics: public Color getColor()

Graphics类方法,用于获得组件的颜色

(2)字体

Font类,以及Graphics类中雨字体有关的方法

名称

描述

public final static int PLAIN

一个代表普通字体风格的常量

public final static int BOLD

一个代表加粗字体风格的常量

public final static int ITALIC

一个代表斜体字体风格的常量

public Font(String name, int style, int   size)

利用指定的字体、风格、大小创建一个font对象

public int getStyle()

返回一个表示当前字体风格的整数值

public Boolean isPlain()

测试一个字体是否是普通字体风格

Graphics:    public Font getFont()

获得当前字体

Graphics:    public void setFont(Font f)

设置当前字体为f指定的字体、风格和大小

 

3.使用Graphics类绘图

Graphics可以绘制字符串或者各种图形。Graphics类常用方法可以参见http://blog.csdn.net/zhliro/article/details/45564251

例:使用Graphics类绘图

代码太长,不再累述。

结果见图1.

1521770825121671.jpg 

图1

4.使用Graphics 2D类绘图

Java2D API:提供了高级的二维图形功能。它可以轻松完成以下功能:

(1)绘制任何宽度的直线;

(2)用渐变颜色和纹理来填充图形;

(3)平移、旋转、伸缩、切变二维图形,对图像进行模糊、锐化等操作;

(4)构建重叠的文本和图形;

(5)对形状进行剪切,将其限制在任意区域内。

Graphics2D类是Graphics类的抽象子类,要使用Java2D API,就必须建立该类的对象。传递给paint方法的对象是Graphics2D的一个子类实例,被向上转型为Graphics类的实例。要访问Graphics2D功能,必须将传递给paint方法的Graphics引用强制转换为Graphics2D引用:

Graphics2D g2d = (Graphics2D)g

 

7.2 Swing基础

1.JFC与Swing

JFC(Java Foundation Classes),Java基础类库,它是关于GUI组建和服务的完整集合。它作为JAVA SE的一个有机部分,主要包含5个内容:

    (1)AWT

(2)Java2D

(3)Accessibility

(4)Drag & Drop

(5)Swing

如上所述,Swing是JFC的一部分,它主要是提供按钮、窗口、表格所有组建,它是纯java组建。

2.Swing与AWT组件

在java.awt包中,包括button、checkbox、scrollbar等,都是Component类的子类,大部分都含有native code,所以随操作系统平台的不同会显示出不同的样子,而且不能更改,是重量级组件。

Swing名称都是在AWT组件名称前加J,都是JComponent类的子类,它是完全由java编写,外观和功能不依赖于任何宿主平台的窗口系统提供的代码,是轻量级组件,可以提供更丰富的视觉感受,但是一些顶层还是重量级的。

3.在Applet和Application中应用Swing

在Applet中应用Swing,就是要将Swing组件加载到Applet容器中(通常是Japplet),这通常在init方法中完成;在application中有,要将Swing组件加载到application的顶级容器中。

例:在Applet中应用Swing

代码:

例:在Application中应用Swing

             

7.3 Swing的层次

Swing组件通常被归为三个层次:顶层容器、中间层容器和原子组件。在介绍之前,先看一下绝大多数Swing组件的继承层次:

java.lang.Object

 – java.awt.Component

   -java.awt.Container

     -javax.swing.JComponent

JComponent类是除了顶层容器以外所有Swing组件的超类,根据继承关系,可以在每个超类中找到大多数GUI组件常用的操作。

Component类中包含了paint、repaint方法,可以在屏幕上绘制组件,大多数GUI组件直接或间接扩展Component。

Container类是容纳相关组件,包括add方法,用来增加组件,包括setLayout方法用来设置布局,帮助Container对象对其中的组件进行定位和设置组件大小。

JComponent类,课定制观感,有快捷键,一般的事件处理功能。

1.Swing的组件和容器层次

(1)顶层容器。它三个类:JFrame实现单个主窗口,JDialog实现一个二级窗口(对话框),JApplet在浏览器中实现applet显示区域。这些都是重量级组件,从继承结构看,它们分别从原来AWT组件的Frame、Dialog和Applet类继承而来。每个使用Swing组件的java程序都必须至少有一个顶层容器,别的组件都必须放在这个顶层容器上才能显示出来。

(2)中间层容器,它存在的目的是为了容纳别的组件,包括两个雷:一般用途的类和特殊用途的类。一般用途的有JPanel、JScrollPane、JSplitPane、JTabbedPane、JToolBar;特殊用途的类有JInternalFrame、JRootPane两类。

(3)原子组件通常是在图形用户界面中和用户进行交互的组件,它的基本功能就是和用户交互信息。根据功能不同,可以分为三类:显示不可编辑信息的JLabel、JProgressBar、JToolTip;用控制功能、可以用来输入信息的JButon、JCheckBox、JRadioButton、JComboBox、JList、JMenu、JSlider、JSpinner、JTexComponet等;还有能提供格式化的信息并允许用户选择的JColorChooser、JfileChooser、JTable、JTree。

7.4 布局管理

在窗口放置不同的组件,需要靠布局管理器来对其自动放置合适的位置。

1.布局管理器

调用容器对象的setLayout方法,并以布局管理器对象为参数,例如:

Container contentPane =frame.getContentPane();

contentPane.setLayout(new FlowLayout());

使用布局管理器可以更容易的进行布局,而且当改变窗口大小时,它还会自动更新版面来配合窗口的大小,不用担心版面因此混乱。

在Java中常用的布局管理器类有很多,经常用到的有以下几种:

(1)BorderLayout(2)FlowLayout(3)FridLayout(4)CardLayout(5)GridBagLayout(6)BoxLayout(7)SpringLayout(8)内容面板(content pane)默认使用的就是BorderLayout,它可以将组建放置到5个区域:东、西、南、北、中。

7.5 内部类

写在别的类体或方法定义中定义的类,它可以访问其外部类中的所有数据成员和方法成员,可对逻辑上相互联系的类进行分组,对于同一个包中的其它类来说,它能够隐藏,可以隐藏一些实现细节,内部类还可以非常方便地编写事件驱动程序。

它的声明方式:

(1)命名的内部类:可在类的内部多次使用

(2)匿名内部类:可在new关键字后声明内部类,并立即创建一个对象。

假设外层类名为Myclass,则该类的内部类名为:

Myclass$c1.class(c1为命名的内部类名)

Myclass$1.class(表示类中声明的第一个匿名内部类)

如果我们想让这个内部类在外面完全看不见,那么我们可以定义一个接口,或者一个抽象类,然后让这个内部接口实现接口、继承抽象类,可以方便实现隐藏实现细节,外部能得到的仅仅是指向超类或者接口的一个引用。

一个内部类可以定义在一个方法里面、任意一个大括号甚至参数列表里面。这样操作可以实现某个接口,产生并返回一个引用;也可以应用在为解决一个复杂问题,需要建立一个类而又不想它为外界所用。

例:匿名的内部类

代码:

代码分析:

    如果我们这个内部类只需要用一次,那么它其实可以练名字都没有。比如说就在cont方法里面return语句中,我们就用这个方法来匿名一个类。这其实是以conents为超类,派生出一个非抽象的类,在这个类中覆盖了contents里面的定义。

7.6 事件处理的基本概念

1.基本知识

GUI程序都是事件驱动的。所谓事件驱动,就是我们看到的大多数程序,在打开的时候会有一些组件,当你操作的时候就会有响应,做出反应。

常见的事件包括:

移动鼠标,单双击鼠标各个按钮,单击按钮,在文本字段输入,在菜单中选择菜单项,在组合框中选择、单选和多选、拖动滚动条,关闭窗口等。

在Swing中是通过事件对象来包装事件,程序可以通过事件对象获得事件的相关信息,对应的程序段可以进行相应的处理。

事件处理的几个要素包括:

(1)事件源。所谓事件源,就是要与用户进行交互的GUI组件,表示事件来自于哪个组件或对象,比如要对按钮被按下这个事件编写处理程序,按钮就是这个事件源。事件源提供注册监听器和取消的方法,如果事件发生,已注册的监听器就会被通知,一个事件可以注册多个事件监听器,每个监听器又可以对多种事件进行响应。

(2)事件监听器。负责监听事件并做出响应,一旦它监视到事件发生,就会自动调用相应的事件处理程序做出响应。事件监听器是一个对象,通过事件源的add***Listener方法注册到某个事件源上,不同的Swing组件可以注册不同的事件监听器,一个事件监听器可以包含对多种具体事件的专用处理方法。

(3)事件对象。封装了有关已发生的事件的信息。

我们只需要关注两件事,一是为事件源注册一个事件监听器,二是实现实践方法。

2.接口与适配器

事件监听器接口:例如MouseListener是一个接口为了在程序中创建一个鼠标事件监听器的对象,我们需要实现其所有的五个方法。

事件监听器适配器类:有时候我们不需要对所有事件进行处理,为此Swing提供了一些适配器类,***Adapter。你关注哪个方法体的具体实现,你就直接覆盖即可。

3.事件处理的方法

(1)实现事件监听器接口。这种方法需要实现接口中所有的方法,对于我们不需要的方法,也要列出来,其方法体使用一堆空的花括号。

(2)继承事件监听器适配器类。只需要重写我们感兴趣的事件。

(3)使用匿名内部类。特别适用于已经继承某个父类。

(4)lambda表达式。对于只有一个抽象方法的函数式监听器接口,也可以使用本方法。

7.7 事件派发机制

在Swing中,这些都不是线程安全的,也就是说不能有多个程序段并发对其操作。在java中有一个事件派发线程,所有事件都由其派发。

1.事件派发机制——事件派发线程

在Swing中专门提供了一个事件派发线程(EDT)用于对组件的安全访问。,它可以用来执行组件事件处理程序的线程(如按钮的点击事件),依次从系统事件队列去除事件并处理,一定要执行完上一个事件的处理程序后,才会处理下一个事件。事件监听器的方法都是在事件派发线程里面执行的,比如ActionListener的actionPerformed方法。

2.事件派发机制——由事件派发线程启动GUI

可以调用invokeLater或者invokeAndWait,请事件分发线程以运行某段代码。要将这段代码放入一个Runnable对象的run方法中,并将该Runnable对象作为参数传递给invokeLater,事件派发线程就会启动这段代码执行。注意,invokeLate是异步执行,不用等代码执行完就返回;invokeAndWait是同步的,要等代码执行完才返回,调用时应避免死锁

例:实现CardLayout

代码:

7.8 顶层容器

Swing这个结构是以顶层容器为根的树状结构,每个组件只能放在一个容器上。Swing有三个顶层容器类,分别是JFrame、JApplet、JDialog,他们都是重量级组件。每个顶层容器都有一个内容面板,通常直接或者间接的容纳别的可视组件。可以由选择为顶层容器添加菜单,菜单位于顶层容器上,但是在内容面板之外的。

(1)JFrame的继承结构:

java.lang.Object

 – java.awt.Component

   -java.awt.Container

     -java.awt.Window

      -java.awt.Frame

       -javax.swing.JFrame

(2)JApplet的继承结构:

java.lang.Object

 – java.awt.Component

   -java.awt.Container

     -java.awt.Panel

      -java.awt.Applet

       -javax.swing.JApplet

(3)JDialog的继承结构:

java.lang.Object

 – java.awt.Component

   -java.awt.Container

     -java.awt.Window

      -java.awt.Dialog

       -javax.swing.J Dialog

只有JApplet是浏览器自动生成,其它都需要我们构造对象,用构造方法去初始化。

例:FrameDemo.java

代码:

例:JOptionPaneDemo.java

    通过静态方法show***Dialog,可以产生四种简单的对话框。他们的方法参数中绝大部分(除了输入对话框可以不指定父窗口)都需要提供一个父窗口组件ParentComponent,只有关闭这些简单的对话框后,才可以返回到其父窗口,也就是说,他们绝大部分都是模态的。

7.9 中间层容器

1.JRootPane

它是唯一一个可以从顶层容器继承过来的,它的层次结构见图2所示。

 1521770906115370.jpg

图2

从图中可以看出,JRootPane上面包括了有glassPane和layeredPane。

    (1)glassPane

    默认状态下是隐藏的,可以使用glassPane截获所有要到达JRootPane别的部分的事件。

(2)layeredPane

分层的每个层都有一个代表层深度的整数值(Z-order),深度值高的组件将覆盖在深度值低的组件上门。它又包括:

contentPane

一般将所有的组件添加在上面

JMenuBar(可选),若没有,则被contentPane充满顶层容器。

2.JPanel

在默认下,除了背景色什么都没有,可以本容易设置边框和绘制特性,有效利用其可以使版面管理更容易,可以使用布局管理器可以管理容纳组件的位置和大小。

3.JScrollPane

容器有滚动条,通过拖动滑块就可以看到更多的内容。它由就各部分组成,包括一个中心显示地带、四个角和四条边。

 1521770926117686.jpg

图3

4. JSplitPane

可以将窗口分为两个两个部分,两个部分操作都是相互联系的。这种分割可以水平、垂直,也可以动态拖拽(拖动分界线时两边组件是否会随着拖拽动态改变大小还是在拖拽结束后才改动)。我们通常先把组件放到Scroll Pane中,再把Scroll Pane放到Split Pane中。这样在每部分窗口中,都可以拖动滚动条看到组件的全部内容。

5. JTabbedPane

如果一个窗口的功能有几项,可以给每项设置一个标签,每个标签下面包含为完成此功能专用的若干组件。

6.JToolBar

将一些常用的功能以工具栏的方式呈现。

7. JInternalFrame(轻量级组件,只能是中间容器)

打开多个子窗口,每个子窗口(文档)各自占用一个新窗口。

7.10 原子组件

    原子组件通常是在图形用户界面中和用户进行交互的组件,它的基本功能就是和用户交互信息。根据功能不同,可以分为三类:显示不可编辑信息的JLabel、JProgressBar、JToolTip;用控制功能、可以用来输入信息的JButon、JCheckBox、JRadioButton、JComboBox、JList、JMenu、JSlider、JSpinner、JTexComponet等;还有能提供格式化的信息并允许用户选择的JColorChooser、JfileChooser、JTable、JTree。

1.显示不可编辑信息的原子组件

(1)JLabel可以显示文字和图像,并且能够指定两者的位置,但不能修改内容。

(2)JProgressBar:在一些软件运行时候的进度条。

(3)JToolTip:使用setToolTipText()方法为组件设置提示信息。有的组件例如JTabbedPane由多个部分组成,需要鼠标在不同部分停留时显示不同的提示信息,这时候可以在其addTab()方法中设置提示信息参数,也可以通过setTooltipTextAt方法进行设置。

2.具有控制功能,可以输入信息的原子组件

(1)Abstract按钮

它是众多按钮类的超类,它是一个抽象类。继承它的类如图4所示。

1521771001708675.jpg 

图4

JButton:普通按钮

JToggleButton:表示有两个选择状态的按钮,其包括CheckBox(多选按钮)和JradioButton(单选按钮)。

JMenuItem:在菜单中使用,它包括JCheckBoxMenuItem(多选按钮)、JRadioButtonMenuItem(单选按钮)和JMenu(一般菜单项)。

(2)JList

JList可以选择一个到多个选项。它由几种各有特色的构造方法(选项是否可以添加、删除),也提供了很多API可以设置各选项的显示方式;考虑到其含有较多选项,经常把它放在一个JScrollPane对象里面。

JList的事件处理一般可以分为两类:

取得用户选取的项目,其事件监听器是ListSelectionListener;

对鼠标事件作出响应,其事件监听器是MouseListener。

(3)JComboBox

在许多选项中选择一个。其有两种方式,可以用下拉列表方式,也可以用更紧凑的方式:

默认情况下是不可编辑的模式,其特色是包括一个按钮和一个下拉列表,用户只能在下拉列表的内容选择一个;

另一种是可编辑,多了一个文本区域,用户可以在文本区域填入列表不存在的内容。

(4)连续数值选择——JSlider和JSpinner

JSlider占空间大,可以设置它的最大、最小、初始值,还可以设置其方向,还可以为其标上刻度或者文本。在JSlider上移动滑动杆,会产生ChangeEvent事件。如图5上侧图片所示。

JSpinner占空间小,类似于可编辑的JComboBox,是一种复合组件,由三个部分组成:向上按钮、向下按钮和一个文本编辑区,它可以通过按钮拉来选择待选项,也可以直接在文本编辑区输入。但是JComboBox不同的是,它的待选项不会显示出来。其如图5的下图所示。

1521771032139414.jpg

图5

(5)文本组件

都继承自JTextComponent抽象类,可分为三类:

①JTextField/JPasswordField/JForattedTextField

只能显示和编辑一行文本,像按钮一样,它们可以产生ActionEvent事件,通常用来接受少量用户输入信息并在输入结束进行一些事件处理。

②JTextArea

可以现实和编辑多行文本,但是这些文本只能是单一风格,通常让用户输入任意长度的无格式文本或者显示无格式的帮助信息。

③JEditorPane/JTextPane

可以显示和编辑多行多种式样的文本,嵌入图像或者别的组件。

3. 提供格式化的信息并允许用户选择的组件

(1)JColorChooser颜色选择对话框

可以让用户选择所需要的颜色。通常使用这个类的静态方法showDialog()来输出标准的颜色选择对话框,其返回值就是选择的颜色。

也可以通过静态方法createDialog()方式输出个性化的颜色选择对话框,例如为其添加菜单、定义其事件处理程序,这个方法的返回值就是一个对话框。

(2)JFileChooser文件选择对话框

它的作用是让用户选择一个已有的文件或者新建一个文件。

可以使用JFileChooser的showDialog()、showOpenDialog()或者showSaveDialog()方法来打开文件对话框,但是它仅仅返回用户选择的按钮(确认还是取消)和文件名(如果确认的话),接下来的要实现的例如存盘或者打开功能还需要程序员自己编写。

这个类提供了专门方法用于设置可选择文件的类型,还可以指定每类文件使用的类型图标。

(3)JTable

用表格展示结构化的数据。它可以为表格设置显示外观(是否有滚动条、调整某一列宽其他列宽变化情形)、显示模式(根据数据类型有不同的排列显示方式、为某一字段添加组合框JComboBox)、选择模式(单选、多选、连续选、任意选等)。

它的事件都是针对表格内容的操作处理,称之为TableModelEvent事件,可以通过addTableModelListener方法为表格添加此种事件监听器。

(4)JTree

用来产生树状结构来直观地表现层次关系,有根节点、树枝节点、树叶节点。

它的构造方法有很多中,参数可以是一个Hashtable,也可以是TreeNode或TreeModel对象。还可以使用JComponent提供的putClientProperty方法来设置JTree的外观,也可以使用TreeCellRenderer来个性化各类节点的显示样式。

7.11 其它Swing特性

1.Action对象

(1)功能

用action封装不同组件的相同功能,也可以封装其它的一些属性。Action接口是对ActionListener接口的一个有用扩展,它的继承关系如下:

public interface Action extends ActionListener

在很多既有菜单又有工具栏的应用程序中,可以通过Action接口封装事件相应代码和相关设置,并添加到不同的事件源中。

还可以通过它对不同组件的显示文字、图标、快捷键、提示文字、是否可用等属性进行统一的设置。

(2)创建Action对象

AbstractAction类实现了Action接口中除了actionPerformed方法以外的其它方法,而且还提供了一些获取和设置Action域属性的方法。

因此,首先有需要创建一个继承抽象来AbstractAction类的子类,然后再实例化这个子类。在子类中我们需要设置需要的属性值、定义actionPerformed方法。

(3)使用Action对象

通过GUI组件的setAction方法将Action对象关联组件。每个具有addActionListener方法的组件也都具有setAction方法。Action是一个事件监听器,如果需要添加多个监听器,应使用addActionListener方法。一个GUI组件可以调用setAction不止一次,但组件和前一个Action对象之间的关联会被删除。

通过setAction方法把Action对象关联到某GUI组件后,会有以下效果:此组件的属性会被设置为符合这个Action对象的属性这个Action对象会被注册为此组件的一个事件监听器对象如果改变了Action对象的属性或方法,那和它关联的组件的属性或方法也会自动变更

2.边框

每个继承自JComponent的Swing组件都可以有边框。

使用组件的setBorder方法为组件添加边框,需要提供一个Border类型的对象。我们可以使用BorderFactory类提供的很多静态方法产生一个常用的Border对象;如果不满足要求,可以直接用javax.swing.border里面的API来定义自己的边框。

3.设置组件观感

在产生任何可视组件以前需要使用UIManager类所提供的setLookAndFeel()静态方法设置好它们的观感。

java提供跨平台的观感。可以利用UIManager类提供的getCrossPlatformLookAndFeelClassName()静态方法获得类名。

程序所处系统的观感。可以利用UIManager类提供的getSystemLookAndFeel()静态方法获得目前操作平台的Look and Feel类名称字符串。

4.设置顶层容器的观感

JFrame和JDialog是重量级组件,依赖于操作系统,当使用的操作系统不同时,所显示的顶层容器就会不同,针对这两个顶层容器,有一个静态方法专门为其设置观感。

static void setDefalutLookAndFeelDecorated(boolean)

说明:

(1)参数是true,使用默认外观。

(2)参数是false,使用操作系统外观。

5.桌面API

从Java 6开始,对于特定的文件类型,Java程序可以和关联该文件类型的主机应用程序进行交互。这种交互是通过java.awt.DeskTop类进行的,因此该类的API叫做桌面API。

桌面API运行Java应用程序完成三件事情:

(1)启用主机平台上默认的浏览器打开URL,这个功能由DeskTop的browse方法完成;

(2)启用主机平台默认的邮件客户端,此功能由DeskTop的mail方法完成;

(3)对特定的文件,启用主机平台上与之相关联的应用程序,对其进行打开、编辑、打印操作,这些功能分别由DeskTop的open、edit、print方法完成。