-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
GUI(图形用户界面)
一、概述
1.什么是GUI?
GUI(Graphical User Interface)是用户与操作系统进行交互的一种方式。该交互方式提供了图形化界面用以给用户对操作系统或软件执行相应命令或操作。
2.java创建GUI的工具:
Java中主要提供了两个包用来创建GUI,即java.Awt包与javax.Swing包。
java.Awt(Abstract Window ToolKit):表示抽象窗口工具包,需要调用本地系统方法实现功能,属于重量级控件。用该工具包创建的GUI会因为操作系统的差异而有所不同。
java.Swing:在AWT的继承上,建立的一套图形界面系统,其中提供了更多的组件,而且完全有java实现。增强了移植性,属于轻量级控件。该工具包创建GUI在任何操作系统上运行都是一样的,即不会受操作系统的差异影响。目前开发主要以java.Swing包为主。
二、架构关系
1.awt工具包的各组件继承关系:
Component:组件
组件1:该类组件可添加组件2中组件或本类组件。
|--Container:容器
|--Window:窗口
|--Frame:框架
|--Dialog:对话框
|--FileDialog:文件对话框
|--Panel:面板
组件2:该类组件不可添加其他组件。
|--Button:按钮
|--Label:标签
|--Checkbox:复选框
|--TextComponent:文本组件
|--TextArea:文本区域,用于多行文本
|--TextField:文本框,仅限于单行文本
Component:表示抽象类,是一个具有图形表示能力的对象,可在屏幕上显示,并可与用户进行交互。
组件1:该类组件可添加组件2中组件或本类组件。
Container:表示容器,可以将组件添加到容器中,添加到容器中的组件放在一个列表中。
Window:是一个没有边界和菜单栏的顶层窗口。窗口的默认布局是 BorderLayout。由于Window中的构造函数需要传入Window对象或Frame对象,且没有提供静态方法获得Window对象或Frame对象,因此不能直接建立对象。
Panel:是最简单的容器类。创建的panel对象,必须装在顶层容器里比如嵌入一个frame里,才能显示。面板的默认布局是FlowLayout 。
Frame:是带有标题和边框的顶层窗口。可以直接创建一个最初不可见的对象,通过设置setVisible方法的值为true可以显示窗体。窗体的默认布局为 BorderLayout。
Dialog:表示对话框,是一个带标题和边界的顶层窗口。Dialog 的默认布局为 BorderLayout。
FileDialog:显示一个对话框窗口,用户可以从中选择文件。
注:组件1中的组件,只有Frame、Dialog和FileDialog组件可自身创建窗体对象,并设置以显示窗体。其中Dialog和FileDialog组件必须依赖于其他窗体组件而存在,如Frame。
组件2:该类组件不可添加其他组件。
Button:此类创建一个标签按钮。当按下该按钮时,应用程序能执行某项动作。
Label:是一个可在容器中放置文本的组件。一个标签只显示一行只读文本。文本可由应用程序更改,但是用户不能直接对其进行编辑。
Checkbox:复选框是一个可处于“开”(true) 或“关”(false) 状态的图形组件。
TextComponent:是所有允许编辑文本的组件的超类。
TextArea:是显示文本的多行区域。可以将它设置为允许编辑或只读。
TextField:是允许编辑单行文本的文本组件。
2.常见组件的图形示例:
1)Frame:框架。
2)Dialog:对话框。
3)FileDialog:文件对话框。
4)Label:标签。
5)TextArea(文本区域)和TextField(文本框)。
6)Checkbox:复选框。
三、常见布局管理器
1.FlowLayout:流式布局管理器。
在纵坐标方向,会从上向下进行放置填充,在横坐标上会进行居中放置填充。当填充不下时,会自动往下进行放置。该管理器是Panel组件的默认布局管理器。
图示:流式布局管理器
2.BorderLayout:边界布局管理器。
容器划分为东、西、南、北、中五个区域,每个区域只能放一个组件,是Frame的默认布局管理器。如果窗体中只有一个组件,将会覆盖整个窗体。
图示:边界布局管理器。
1)可以把组件放在这五个位置的任意一个,如果未指定位置,则缺省的位置是CENTER。
2)南、北位置控件各占据一行,控件宽度将自动布满整行。东、西和中间位置占据一行,若东、西、南、北位置无控件,则中间控件将自动布满整个屏幕。若东、西、南、北位置中无论哪个位置没有控件,则中间位置控件将自动占据没有控件的位置。
3.GridLayout:网格布局管理器。
容器的空间划分成M×N列的网格区域, 每个区域只能放置一个组件,类似规则的矩阵。
图示:网格布局管理器。
1)使容器中的各组件呈M行×N列的网格状分布。网格每列宽度相同,等于容器的宽度除以网格的列数。网格每行高度相同,等于容器的高度除以网格的行数。
2)各组件的排列方式为:从上到下,从左到右。组件放入容器的次序决定了它在容器中的位置。容器大小改变时,组件的相对位置不变,大小会改变。
3)设置网格布局行数和列数时,行数或者列数可以有一个为零。若rows为0,cols为3,则列数固定为3,行数不限,每行只能放3个控件或容器。若cols为0,rows为3,则行数固定为3,列数不限,且每行必定有控件,若组件个数不能整除行数,则除去最后一行外的所有行组件个数为:Math.ceil(组件个数/rows)。
Math.ceil(double x):传回不小于x的最小整数值。比如行数为3,组件数为13个,则Math.ceil(13/3)=5,即第一行,第二行组件数各为5个,剩下的组件放在最后一行。
4)若组件数超过网格设定的个数,则布局管理器会自动增加网格个数,原则是保持行数不变。
4.CardLayout:卡片布局管理器。
卡片布局能够让多个组件共享同一个显示空间,共享空间的组件之间的关系就像一叠牌,组件叠在一起,初始时显示该空间中第一个添加的组件,通过CardLayout类提供的方法可以切换该空间中显示的组件。
5.GridBagLayout:网格包布局管理器。
GridLayout的升级版,组件仍然是按照行、列放置,但是每个组件可以占据多个网格,类似非规则矩阵。
图示:网格包布局管理器。
四、事件监听机制
1.组成:监听器、事件源、事件对象、事件处理方式。
监听器:将可以触发某一个事件的动作(可以存在多个动作)都已经封装到监听器中。
事件源:指awt包或swing包中的那些图形界面组件。
事件:每一个事件都有自己特有的对应事件和共性事件。
事件处理方式:对发生的事件进行针对性的处理。
注:监听器、事件源和事件在java中已经封装好了,直接创建和获取即可。
2.事件监听流程图:
执行过程:将监听器注册到事件源上,当外部动作触发到事件源上动作时,产生一个事件对象并传给监听器,监听器用对应的事件处理方法对事件对象进行针对性的处理。
3.问题思考:
1)为什么运行java程序时,窗口监听器中的方法windowOpened事件会发生不能被执行的情况?
答:因为窗口打开事件仅在第一次使窗口可见时才传递此事件,因此可视化操作应放在添加监听器的后面执行,才能产生windowOpened事件。
4.代码练习:
练习1:窗体事件演示。
import java.awt.*;
import java.awt.event.*;
class WindowEventDemo1
{
public static void main(String[] args)
{
//创建框架对象
Frame f=new Frame();
//设置框架的横向与纵向长度
f.setSize(500,400);
//设置框架距屏幕的横向与纵向的距离
f.setLocation(300,200);
//设置框架为流式布局
f.setLayout(new FlowLayout());
//创建按键组件
Button b=new Button("一个按钮");
//把按钮组件添加到框架
f.add(b);
//向框架添加窗口监听器
f.addWindowListener(new WindowAdapter()
{
public void windowOpened(WindowEvent e)
{
System.out.println("窗口打开"+e.toString());
}
public void windowActivated(WindowEvent e)
{
System.out.println("窗口激活");
f.setLocation(0,0);
}
public void windowClosing(WindowEvent e)
{
System.out.println("窗口关闭");
System.exit(0);
}
public void windowDeactivated(WindowEvent e)
{
System.out.println("窗口停用");
f.setLocation(300,200);
}
});
//可视化操作应放在添加监听器的后面,否则windowOpened事件将不被执行
f.setVisible(true);
}
}
程序运行的结果如下截图所示:
当窗口界面弹出的时候,控制台输出“窗口打开”并且打印出窗口打开事件的相关信息。
当窗口界面被激活时,控制台输出“窗口激活”,并且设置窗口界面距屏幕左上角的横向与纵向距离都为0。
当窗口界面未被激活时,控制台输出“窗口停用”,并且设置窗口界面的距屏幕左上角的横向与纵向距离分别为300和200。
当窗口界面被关闭时,控制台输出“窗口关闭”,窗口界面消失。
练习2:Action事件演示示例。
import java.awt.*;
import java.awt.event.*;
class ActionEventDemo
{
public static void main(String[] args)
{
new ActionDemo();
}
}
class ActionDemo
{
//定义该窗体所需组件的引用
private Frame f;
private Button but;
//初始化构造函数
ActionDemo()
{
init();
}
//创建窗体与功能实现
public void init()
{
f=new Frame();
//设置窗体的横向与纵向长度,并设置窗体距屏幕左上角的横向与纵向距离
f.setBounds(300,100,500,400);
f.setLayout(new FlowLayout());
but=new Button("一个按钮");
f.add(but);
myEvent();
f.setVisible(true);
}
//对窗体及内部组件注册事件监听
public void myEvent()
{
//对窗体注册事件监听
f.addWindowListener(new WindowAdapter()
{
public void windowOpened(WindowEvent e)
{
System.out.println("窗口打开");
}
});
//对按钮注册事件监听
but.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("关闭窗口");
System.exit(0);
}
});
}
}
程序运行后的生成的窗体截图如下图所示:
当用鼠标点击按钮时,则在控制台会输出“关闭窗口”,并把整个窗体关闭。
练习3:鼠标事件演示示例。
import java.awt.*;
import java.awt.event.*;
class MouseEventDemo
{
public static void main(String[] args)
{
new MouseDemo();
}
}
class MouseDemo
{
private Frame f;
private Button but;
MouseDemo()
{
init();
}
public void init()
{
f=new Frame();
f.setBounds(300,100,500,400);
f.setLayout(new FlowLayout());
but=new Button("my button");
f.add(but);
myEvent();
f.setVisible(true);
}
//定义事件监听
public void myEvent()
{
//向窗体注册窗体事件监听
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.out.println("窗口关闭");
System.exit(0);
}
});
//向按钮注册鼠标事件监听
but.addMouseListener(new MouseAdapter()
{
// public void mouseEntered(MouseEvent e)
// {
// System.out.println("鼠标进入");
// }
//
// public void mouseExited(MouseEvent e)
// {
// System.out.println("鼠标离开");
// }
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount()==2)
{
System.out.println("双击鼠标");
}
}
// public void mousePressed(MouseEvent e)
// {
// System.out.println("鼠标按下");
// }
//
// public void mouseReleased(MouseEvent e)
// {
// System.out.println("鼠标释放");
// }
});
}
}
程序运行后的结果如下截图所示:
当双击窗口界面的“my button”按钮时,控制台输出“双击鼠标”。
当点击窗口界面的关闭按钮时,控制台输出“窗口关闭”,并且整个窗体消失。
练习4:键盘事件演示示例。
import java.awt.*;
import java.awt.event.*;
class KeyEventDemo
{
public static void main(String[] args)
{
new KeyDemo();
}
}
class KeyDemo
{
private Frame f;
private Button but;
private TextField tf;
KeyDemo()
{
init();
}
public void init()
{
f=new Frame();
f.setBounds(300,100,500,400);
f.setLayout(new FlowLayout());
but=new Button("my button");
tf=new TextField(20);
f.add(tf);
f.add(but);
myEvent();
f.setVisible(true);
}
public void myEvent()
{
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.out.println("窗口关闭");
System.exit(0);
}
});
//向文本框组件注册键盘事件监听
tf.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
//当按下ctrl+enter组合键时,退出
if (e.isControlDown()&&e.getKeyCode()==e.VK_ENTER)
{
System.exit(0);
}
// //输出键的字符形式和整数形式,仅限于单个字母
// System.out.println(e.getKeyChar()+"---"+e.getKeyCode());
// //输出键的字符形式和整数形式,更为广泛
// System.out.println(KeyEvent.getKeyText(e.getKeyCode())+"---"+e.getKeyCode());
}
// public void keyReleased(KeyEvent e)
// {
// System.out.println("键盘释放");
// }
});
}
}
程序运行后的结果如下截图所示:
当向文本框输入内容时,如果键盘按下组合键“ctrl+enter”,则整个窗体界面消失。
当鼠标点击窗体关闭按钮时,控制台输出“窗口关闭”,并且整个窗体消失。
练习5:列出指定目录的所有内容。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
class WindowTest
{
public static void main(String[] args)
{
new MyWindow();
}
}
class MyWindow
{
private Frame f;
private TextField tf;
private TextArea ta;
private Button but;
MyWindow()
{
init();
}
public void init()
{
f=new Frame();
f.setBounds(300,200,600,400);
f.setLayout(new FlowLayout());
tf=new TextField(50);
ta=new TextArea(20,70);
but=new Button("转到");
f.add(tf);
f.add(but);
f.add(ta);
myEvent();
f.setVisible(true);
}
//事件处理
public void myEvent()
{
//对窗体注册窗口监听器
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
//对按钮注册活动监听器
but.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
showDir();
}
});
//对文本框注册键盘监听器
tf.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode()==e.VK_ENTER)
{
showDir();
}
}
});
}
//列出文本框中指定目录的内容
public void showDir()
{
//获取文本框的内容
String dir=tf.getText();
File file=new File(dir);
if (file.exists()&&file.isDirectory())
{
//清空文本区域的内容
ta.setText("");
String[] str=file.list();
for(String s:str)
{
//将遍历的内容追加到文本区域中,并进行换行
ta.append(s+"\t\n");
}
//清空文本框的内容
tf.setText("");
}
}
}
程序运行后的结果如下截图所示:
当在文本框输入目录“E:\heima”时,点击转动按钮,则在文本区域中列出了该目录的所有内容。运行结果如下截图所示:
五、菜单
1.继承关系:
MenuComponent:菜单组件
|--MenuBar:菜单栏
|--MenuItem:菜单项
|--Menu:菜单
MenuComponent:表示菜单组件,是所有与菜单相关的组件的超类。
MenuBar:表示菜单栏,该类封装绑定到框架的菜单栏的平台概念。通过调用该框架的 setMenuBar 方法,将菜单栏与 Frame 对象关联。
MenuItem:表示菜单项,菜单中的所有项必须属于类 MenuItem 或其子类之一。
Menu:表示菜单,是MenuItem的子类,是从菜单栏部署的下拉式菜单组件。它可以是 MenuItem 的一个实例、子菜单(Menu 的一个实例)、或复选框(CheckboxMenuItem 的一个实例)。
2.组件图示:
特点:
1)菜单栏依赖于框架存在,框架添加菜单栏需调用SetMenuBar方法。而菜单栏添加菜单或菜单中添加菜单项都调用add方法。
2)菜单中即可以添加菜单项也可以添加菜单。菜单中添加的菜单称为子菜单,子菜单中可以继续添加子菜单或菜单项。
3)菜单栏、菜单、菜单项都可以注册相应的监听器。
4)菜单栏添加菜单的先后顺序反应到图形化界面中为从左到右的排列顺序。菜单添加子菜单或菜单项的先后顺序反应到图形化界面中为从上到下的排列顺序。
代码示例:
import java.awt.*;
import java.awt.event.*;
class MyMenuDemo
{
public static void main(String[] args)
{
new MyMenu();
}
}
class MyMenu
{
//定义所需组件的引用
private Frame f;
private TextArea ta;
private MenuBar mb;
private Menu m,subm,edit,help;
private MenuItem close,submi,open;
//构造函数
MyMenu()
{
init();
}
//窗体设置和功能实现
public void init()
{
f=new Frame();
f.setBounds(300,200,600,400);
ta=new TextArea();
//创建菜单栏
mb=new MenuBar();
//创建菜单
m=new Menu("文件");
subm=new Menu("子菜单");
edit=new Menu("编辑");
help=new Menu("帮助");
//创建化菜单项
open=new MenuItem("打开");
close=new MenuItem("退出");
submi=new MenuItem("子条目");
f.add(ta);
//在框架上添加菜单栏
f.setMenuBar(mb);
//在菜单栏上添加菜单
mb.add(m);
mb.add(edit);
mb.add(help);
//在菜单上添加子菜单和菜单项,添加的顺序不同,显示效果也不同
m.add(open);
m.add(subm);
m.add(close);
//在子菜单上添加子条目
subm.add(submi);
myEvent();
f.setVisible(true);
}
//事件处理
public void myEvent()
{
//对窗体注册事件监听器以关闭窗体
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
程序运行后的效果如下截图所示:
练习1:打开和保存文件。
可实现打开任意纯文本文件,并将内容显示窗体的文件区域中。可对窗体文本区域的内容进行任意修改并保存到指定的目录文件中。基本实现类似记事本的打开保存功能。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
class MyMenuTest
{
public static void main(String[] args)
{
new MyMenu();
}
}
class MyMenu
{
//定义所需的组件引用
private Frame f;
private TextArea ta;
private MenuBar mb;
private Menu m;
private MenuItem close,open,save;
private FileDialog diaOpen;
private FileDialog diaSave;
private File file;
MyMenu()
{
init();
}
public void init()
{
f=new Frame();
f.setBounds(300,200,600,400);
ta=new TextArea();
//创建菜单栏
mb=new MenuBar();
//创建菜单
m=new Menu("文件");
//创建化菜单项
open=new MenuItem("打开");
save=new MenuItem("保存");
close=new MenuItem("退出");
f.add(ta);
//在框架上添加菜单栏
f.setMenuBar(mb);
//在菜单栏上添加菜单
mb.add(m);
//在菜单上添加子菜单和菜单项,添加的顺序不同,显示效果也不同
m.add(open);
m.add(save);
m.add(close);
//事件处理
myEvent();
f.setVisible(true);
}
//事件处理
public void myEvent()
{
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
//对open菜单项注册活动监听器,实现文件打开功能
open.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//创建文件对话框对象,指定依存的框架f,指定模式属性为LOAD.
diaOpen=new FileDialog(f,"打开文件",FileDialog.LOAD);
//显示文件对话框
diaOpen.setVisible(true);
//获取文件对话框的目录
String dir=diaOpen.getDirectory();
//获取选定的文件
String name=diaOpen.getFile();
//如果获取到的目录或文件名为null,则退出程序
if (dir==null||name==null)
return;
//如果目录和文件名都不为null,则清空文本区域内容
ta.setText("");
file=new File(dir,name);
BufferedReader bufr=null;
try
{
bufr=new BufferedReader(new FileReader(file));
String line=null;
while ((line=bufr.readLine())!=null)
{
ta.append(line+"\r\n");
}
}
catch (IOException ex)
{
throw new RuntimeException("打开文件失败");
}
finally
{
if (bufr!=null)
{
try
{
bufr.close();
}
catch (IOException ex)
{
throw new RuntimeException("读取关闭失败");
}
}
}
}
});
//对save菜单项注册活动监听器,实现文件保存功能
save.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//如果file指向null,则创建file对象
if (file==null)
{
//创建文件保存对话框,指定依存的框架f,指定属性为SAVE
diaSave=new FileDialog(f,"文件保存",FileDialog.SAVE);
//显示文件保存对话框
diaSave.setVisible(true);
//获取文件保存对话框的父目录及选定文件名
String dir=diaSave.getDirectory();
String name=diaSave.getFile();
if (dir==null||name==null)
return;
//根据获取的父目录和文件名,创建文件对象
file=new File(dir,name);
}
BufferedWriter bufw=null;
try
{
bufw=new BufferedWriter(new FileWriter(file));
//获取文本区域中的内容
String str=ta.getText();
//将文本区域中的内容写入缓冲区
bufw.write(str);
// bufw.flush();
}
catch (IOException ex)
{
throw new RuntimeException("保存失败");
}
finally
{
if (bufw!=null)
{
try
{
bufw.close();
}
catch (IOException ex)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
});
close.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
}
}
双击jar包执行可以像图形化界面那样,通过双击程序即可执行,不必再通过命令行的方式执行程序。
实现步骤如下:
1)创建包。如果java源程序中有创建包,可跳过。如果没有,需要创建一个包,如“pakage mymenu”。
2)打包。给源程序进行打包,在控制台运行命令“javac -d E:\heima MyMenuTest.java",则在“E:heima”目录下的生成一个mymenu包文件夹,在该文件夹下有所有的.class文件。
3)在 mymenu包文件夹所在目录,新建一个任意名称任意类型的文件,如“MyMenuTest.txt”。在MyMenuTest.txt文件中写入指定格式内容:“Main-Class: mymenu.MyMenuTest”,只需写入引号内的内容即可。格式应注意两点:1.在冒号后应有一个空格存在;2.在文件末尾需进行回车。
4) 在控制台运行命令:jar -cvfm MyMenuTest.jar MyMenuTest.txt mymenu",可看到在“E:heima”目录下生成了一个可执行文件MyMenuTest.jar。如果想添加其他信息,则直接运行jar命令,即可得到相应的命令的帮助信息。
5)双击MyMenuTest.jar,即可运行程序。
注意事项:
1)在固定格式中:
a)如果无空格:在编译的时候,就会报IO异常,提示无效的头字段,即invalidheader field。这说明MyMenuTest.txt在被IO流读取。
b)如果无回车:在列表清单.MF中不会加入相应的加载主类的信息,也就是说配置清单的属性主类名称不会加载进清单中,也就不会执行。
2)jar文件必须在系统中注册,才能运行。