一. GUI简介
1. GUI的前世今生
GUI是图形用户接口的意思,全称为Graphical user Interface,是用图形界面的方式显示计算机操作的界面,方便直观,我们只需动用鼠标键盘操作窗口文本就可以实现我们想要的内容,现在我们使用的Windows操作系统就是GUI图形界面,Windows本身就是视窗的意思,Windows的出现可谓改变了电脑的命运。
在GUI之前使用的是CLI,全称Command lineUser Interface为命令行用户接口,就是常见的DOS命令行操作,需要我们记住常见的DOS命令,这样的操作并不直观,在Linux操作系统早期的时候就是使用CLI操作系统,但是这样的操作系统需要使用者记住大量的操作指令来控制电脑,所以电脑的使用率也仅限于一小部分人。
GUI的出现使得人们对电脑的操作简单起来,比如我们在创建文件夹时,CLI得去DOS命令行输入md命令,而GUI则直接用鼠标点击创建新文件夹就可以了,这样的方式极大的形象化了对电脑的操作,让更多的人能够去操作电脑,正是因为图形化界面的出现电脑才可以如此迅速的走进千家万户。
2. java中的GUI
(1)java作为一编程语言,他也能够建立图形化界面,java中为GUI提供的了很多类,这些类都存在于java.awt和javax.swing包当中。
Java的awt包是Abstract Window Toolkit(抽象窗口工具包)的缩写,这个包中的类需要使用到电脑的系统方法,也就是我们通过java来调用操作系统的底层资源来实现图形化界面的创造,不同的操作系统下界面可能会有差别,这个包中的控件我们称为重量级控件。
javax.swing包是在java.awt的基础上建立的一套图形界面系统,其中提供了更多的组建,而且完全有java来实现,这个包的出现增强了可移植性,使用该包开发出的界面无论放在哪种操作系统中都无差别,属于轻量级的控件。
注意点是图形化界面由单独的线程来运行的,在主线程中开启图形化界面的时候,如果图形化界面线程不结束,即使主线程结束了JVM也不会退出。
(2)Java中GUI的常见体系如下:
Component是组件的意思,GUI包括很多的组成部分,比如说按钮啊,面板啊,输入框等等,Component就是就是这些组件向上抽取而来的根类,Container是容器的意思,他是一个特殊的组件,这个组件可以通过add方法添加其他组件。
二. 布局管理器
布局管理器就是界面中组件的排列方式,是整体的布局,常见的布局管理器有5种。如果想在一个界面中掺杂多种布局,需要首先把这个界面切成多个Panel面板,在不同的面板中使用不同的布局方式。
(1) FlowLayout:流式布局管理器
所有的组件都是按照从左到右的排列顺序排列的,第一行组件装满后才装到下一行,这种布局管理器是Panel(面板)默认的布局管理器。
(2) BorderLayout:边界布局管理器
按东南西北中排列,在添加组件的时候需要指定添加到容器的哪个方位,如果没有指定,该组件会把容器的空间全部覆盖掉,比如一个按钮占了整个布局。
该布局管理器是Frame(框架)的默认布局。
(3) GridLayout:网格布局管理器
规则的矩阵排列方式,就是先把容器分成网格,一个组件占一个格子。
(4) CardLayout:卡片布局管理器
是选项卡,就是一个选项对应着一个布局内容。
(5) GriolBagLayout:网格包布局管理器
非规则的矩阵,就是一个容器被分割成网格之后,一个组件可能占多个格子,这样就不是一个标准的网格了。
三. Component体系下的常用方法
1.Component类常用方法
Component是awt包中的对象,他是GUI中的组件的超类,这个类中提供了组件的基本的方法。
我们来说一说其常见的方法
1> setVisible(Boolean b)
根据参数b的值显示或隐藏此组件
2> setSize(int width,int height)
根据传递进来的宽、高值调整组件的大小
3> setLocation(int x,int y)
设置本地位置,x是距离屏幕左侧,y是距离屏幕上方
4> setBounds(int x,int y,int width,int heigh)
设置本地位置与窗体的大小
2. Container类常见方法
Container是Component类中较为特殊的一个组件,这个组件可以添加其他的组件,其原理就是Container内部封装了一个集合,他可以将其他的组件对象添加到集合中来
1> add(组件对象)
此方法有多种重载形式,可以指定添加对象到这个容器的尾部还是指定位置上。
2> setLayout(LayoutManager mgr)
设置布局管理器,让这个容器按什么布局排列
下面我们来看一看怎么样建一个图形界面的:
class GUIDemo1 {
public static void main(String[] args) {
//构造一个最初不可见的Frame框架,标题为“我的电脑”
Frame f = new Frame("我的电脑");
//设置此框架为可见
f.setVisible(true);
//设置窗体的大小
f.setSize(500, 550);
//设置本地位置
f.setLocation(300,100);
//按照流式布局设置该页面
f.setLayout(new FlowLayout());
//添加一个按钮
f.add(new Label("确定"));
//添加一个复选框
f.add(new Checkbox("我是复选框"));
//添加一个文本标签
f.add(new Label("hello world!"));
}
}
当我们运行程序就会得到这么一个界面:
3. 创建图形化界面的步骤
我们说明一下图形化界面所要做的步骤:
1> 创建一个原始的窗体
2> 对窗体进行基本设置,比如大小、位置、布局
3> 定义组件
4> 将组件添加到窗体中,add()方法
5> 让窗体显示setVisible(true)
四. 事件监听机制
1.事件监听机制的说明
事件监听机制是图形化界面中一个非常重要的,当我们操作一个组件的时候这个组件对应着各种事件,比如一个窗口,他就对应着鼠标单击事件,键盘录入事件等等,所以就出现了事件监听机制这么一种逻辑。
事件监听机制由四部分构成:
1> 事件源:就是组件,这是事件发生的源头
2> 事件:每一个事件源都有着自己特有的事件和共性的事件
3> 监听器:将可以触发某一事件的动作封装到监听器中,这个监听器是用来检测事件是否发生的,要注意可以引发事件的动作有很多不只是一种。
4> 事件的处理:当事件发生后,怎样处理需要我们来指定的。事件源、事件和监听器在实际开发时都已经被封装好了,而事件是如何处理的使我们自己编写的,所以在图形化界面中关于事件的处理是很重要的内容。
我们来看一看事件监听机制的流程:
2.添加监听器
(1)通过上面的对事件监听机制的了解,我们知道了在此机制中需要我们操作的就是对处理动作的描述,这个描述是写在监听器中的,因此监听器使我们需要牢牢掌握的东西。
添加监听器最为重要的一点就是我们要操作哪个组件就去找哪个组件的监听器,因为监听器要注册到组件上,所有的监听器后缀名都是Listener。
(2)我们来说一说怎么来添加监听器,在最开始的例子中,我们做了一个最简单的图形界面,这个界面完全是为了演示java可以做图形化界面,然后就毫无用处了,这个界面甚至都不能关闭,这是因为我们所设计的这个窗口没有添加监听器,他不能够检测到鼠标点击关闭按钮,现在我们通过添加监听器来监听关闭事件,并且鼠标点击按钮确定会出现新的内容。
①我们想要把窗口关闭,就去查窗口的方法,在API文档中打开Window类,发现方法:
addWindowListener(WindowListener w)
这个方法就是给窗口添加监听器的方法。他需要接收一个WindowListener类型的参数,这个WindowListener是一个接口,他具有7个方法,都是对窗口进行操作的,而我只想复写他的关闭窗口的方法没有必要全部复写他的方法,在我们不断的找寻中发现他有个子类WindowAdaper,这个子类是抽象的,但是一个抽象的方法都没有,也就是说他复写了接口中的7个方法,但是却没有在方法体中写内容,这个类的出现就是为了我们能便捷的使用这7个方法,只要我们定义一个类复写WindowAdaper中我们所需要的方法就可以使用了,像这种没有抽象方法的抽象类我们称为适配器。
②添加按钮监听器,就去Button中寻找监听器,发现:
addActionListener(ActionListenner a),再看一看ActionListener,他是一个接口,但是他只有一个方法,所以我们直接实现这个接口就可以使用了,注意大多数监听器接口方法都是很多的,但是都会像WindowListener下的WindowAdapter一样给我提供一个没有抽象方法的抽象类供我们使用。
看一看如何来使用监听器:
class GUIDemo3
{
private Frame f;
private Button but;
//在对象初始化时调用图形界面的初始化
GUIDemo3()
{
init();
}
//图形界面初始化
public void init()
{
f = new Frame("我的窗体");
but = new Button("退出");
//进行基本信息设置
f.setLayout(new FlowLayout());
f.setBounds(300,100,300,300);
f.add(but);
//添加监听器
listener();
//图形显示
f.setVisible(true);
}
//把事件监听器写在此方法中
public void listener()
{ //添加窗口监听事件,通过匿名内部类的形式
f.addWindowListener(new WindowAdapter()
{
public void windowOpened(WindowEvent e)
{
System.out.println("open:我被打开了");
}
public void windowClosing(WindowEvent e)
{
System.out.println("窗口关闭的");
System.exit(0);
}
public void windowActivated(WindowEvent e)
{
System.out.println("苏醒");
}
});
/*让按钮具备退出程序的功能
事件源:按钮
选择监听器:
我们关闭窗口时候,我们把监听器添加到frame上,现在我们要
操作按钮,我们去找按钮的方法,找到addActionListener
*/
but.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
System.out.println("按钮关闭的");
System.exit(0);
}
});
}
//主函数初始化对象,来完成图形化界面的构建
public static void main(String[] args)
{
new GUIDemo3();
}
}
当此程序开始运行时,图形化页面启动,控制台会输出:“苏醒 open:我被打开了”,当我们的程序每次被前置时,windowActivated方法都会调用,在控制台输出“苏醒”,最后我们点击关闭按钮,程序结束,控制台输出“窗口关闭的”,我们也可以点击按钮“退出“,这时程序也会退出,控制台打印”按钮退出的“
3. 事件监听机制的事件
(1) 窗口事件:WindowEvent
我们已经了解到一些窗口事件,比如窗口的前端显示、窗口最小化、窗口关闭等,窗口的监听器是添加在窗口类Window上。
(2)动作事件ActionEvent
首先要明确一点就是动作事件不代表一个具体的动作,他是一种语义,比如某个按钮本鼠标点击,点击文本框按下回车符等等。动作事件一般都添加在一些
(3)鼠标事件MouseEvent
鼠标事件包括很多,比如说:mouseEntered(MouseEvent m)鼠标悬停、MouseExited(MouseEvent m)鼠标离开当前组、MouseClicked(MouseEvent m)单击事件等等。
这些是鼠标事件中的一部分,MouseEvent当中含有更多的关于获取鼠
标操作的方法,比如说getClickcount()获取点击次数
当鼠标事件的单击事件与动作事件事件作用于一个组件上时,比如一个Button按钮上既添加了鼠标单击事件,又添加了动作事件,鼠标单击事件先执行。
(4)键盘事件KeyEvent
在KeyEvent当中定义了很多静态的字段,这些字段是每一个键所对应的数值,这极大的方便了我们的使用。
当我们获取到一个键按下时在绑定的组件上就可以获取到该键的值,通过getKeyChar()可以获取到键的字符,比如键A对应“a”,利用getKeyText()可以获取到这个键对应的文本,如回车键对应“enter”;利用getKeyCode()可以获取到键所对应的数值,我们获取到键盘事件后就可以自定义处理机制来完成对该事件的处理。
鼠标事件和键盘事件是公共事件,也就是说监听器的方法定义在Component中,这样每个组件都可以获取到公共事件的监听器,我们通过一个小demo来演示一下这些事件的应用:
class GUIDemo4
{
GUIDemo4(){
init();
}
private Frame f;
private Button but;
private TextField tf;
private Label lb;
//界面初始化
public void init()
{
f = new Frame("小宏");
but = new Button("退出");
//定义一个单行文本输入框
tf = new TextField(33);
f.setBounds(300,100,400,300);
f.setLayout(new FlowLayout());
f.add(but);
f.add(tf);
listener();
f.setVisible(true);
}
//监听事件
public void listener()
{
//Frame窗体添加窗口监听器
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
//按钮添加动作监听事件
but.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent a)
{
System.out.println("按钮让关的");
System.exit(0);
}
});
//按钮添加鼠标监听器 ,利用匿名内部类继承适配器
but.addMouseListener(new MouseAdapter(){
//定义计数器记录鼠标悬停的次数
private int count = 1;
//鼠标悬停在按钮上时
public void mouseEntered(MouseEvent m){
System.out.println("鼠标来啦!!!"+count++);
}
//鼠标离开按钮时
public void mouseExited(MouseEvent m){
System.out.println("鼠标走了!!!");
}
//鼠标点击按钮时
public void mouseClicked(MouseEvent m){
System.out.println("单击动作");
//鼠标双击
if(m.getClickCount()==2){
System.out.println("双击动作");
}
}
});
//按钮添加键盘监听器
but.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent k)
{
//getKeyChar()返回与键相关联的字符
//getKeyCode()返回与此事件键关联的整数
System.out.println("Code:"+k.getKeyCode()+"...."+"char:"+k.getKeyChar());
//getKeyText(int keyCode)根据相关整数获取对应的Stirng文本
System.out.println("获取字符:"+new String(k.getKeyText(k.getKeyCode())));
//组合键
if(k.isControlDown()&&k.getKeyCode()==KeyEvent.VK_ENTER){
System.out.println("Control+enter is run");
}
}
});
//单行文本框添加键盘监听器
tf.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent k){
//获取键所对应的数值
int code = k.getKeyCode();
//将获取到的数值与字段值相比
if(!((code>=KeyEvent.VK_0)&&(code<=KeyEvent.VK_9))){
System.out.println(code+"....非数字,不能显示");
/*原来当键盘按下就输入到文本框中,现在调用consume方法来结束默认操作
也就是键盘按下后不符合要求就不录入到文本框中*/
k.consume();
}
}
});
}
public static void main(String[] args)
{
new GUIDemo4();
}
}
在这个例子中,Button按钮上有3个监听器:①动作监听器是当鼠标点击按钮,程序退出;②鼠标监听器当鼠标悬停,控制台打印:“鼠标来了”,鼠标移开,打印“鼠标走了”;③键盘监听器:当此按钮是当前事件源的时候,我们按键,控制台打印此键的字符值和数值。
TextField单行文本输入框有一个监听器就是键盘监听器,当键盘录入的值为数字时,就显示在文本框中,如果不是数字,在控制台打印“字符对应的数值……非数字,不能显示”。
五.GUI相关组件
1.文本框
文本框分为单行文本和多行文本,他们有一个共同的父类TextComponent,单行文本类是TextField,多行文本是TextArea,文本框是图形化界面中非常常见的组件,我们可以通过文本框获取数据,也可以通过文本框输出内容。
在TextComponent中有两个常用方法:
1> setText(String s)
往文本框中写入字符串
2> getText()
获取文本框中的内容,是一个字符串
在TextArea中有一个方法append(String str)。这个方法可以将字符串写入到文本框的尾部,实现文本的续写
2.对话框
(1)提示对话框
在Windows操作系统中,我们在使用中有时会弹出一个提示窗口,这个提示窗口就叫做对话框。对话框在java中是通过类Dialog来封装的,要了解对话框必须先明确对话框是一个单独的组件还是一个容器。
对话框我们分析一下,如果我有一个信息需要提醒给操作者,我们会把信息显示在图形界面中,利用label标签就可以做到,但是如果我们只是使用标签的话无法做到这个信息的指定显示,所以对话框是一个容器,这个容器中国装载了其他组件,当用户需要操作此容器时,这个容器才显示出来,因此对话框一般也要与其他的容器相关联。
Dialog类就是用来描述对话框的,这个类的构造方法很多,其中有一个较为常见:Dialog(Frame f,String str,boolean modal)
Frame是指定此对话框的所有者,对话框一般都是和其他窗体关联在一起的;str是此对话框的标题,modal是模式,当modal为true时,当对话框出现后与他相关联的窗体就无法操作了。
(2)文件对话框
文件对话框就是我们平时点击保存文件或者打开文件时提示的对话框,在这个对话框中可以选择文件,在java中把文件对话框封装成了一个类:FileDialog,他是对话框Dialog的子类。
FileDialog的属性在初始化的时候就可以确定,因为你要确定你创建的这个文件对话框到底是保存还是关闭,于是就用到了两个常量,这两个常量FileDialog都已经封装好了:
FileDialog.LOAD打开文件
FileDialog.SAVE保存文件
我们可以通过构造方法来选择该对话框是打开文件还是关闭文件。
3.菜单
菜单也是图形化界面中常见的选项,菜单我们可以看做一个个菜单项的集合,比如一个“文件”菜单对应着“打开”、“保存”、“另存为”等等子选项,我们来看一看java中关于菜单的分类:
菜单栏中可以添加菜单,而一个菜单中既可以添加菜单项,也可以继续添加菜单,菜单项是最终的选项。在制作菜单一定要条理清楚,把他们的所属关系列清,这样才能高效的开发出界面。
我们做一个简单的图形界面山寨一个笔记本应用程序,这个小程序可以录入文字,并且可以保存在电脑上,还能打开其他的文本文件,这个示例涉及到IO技术和图形化界面的相关内容:
class GUIMenuDemo
{
GUIMenuDemo()
{
init();
}
//定义界面所需组件
private Frame f;
private MenuBar mb;
private Menu me_fiel;
private MenuItem mi_open,mi_save,mi_clo;
private TextArea ta;
private FileDialog fd_open,fd_save;
private Dialog d;
private Button but;
private Label l;
//界面随着对象的的初始化而加载
private void init() {
//初始化界面
f = new Frame("山寨记事本");
f.setBounds(300, 200, 500, 400);
f.setLayout(new BorderLayout());
fd_save = new FileDialog(f,"保存",FileDialog.SAVE);
fd_open = new FileDialog(f, "打开", FileDialog.LOAD);
//初始化各组件
mb = new MenuBar();
ta = new TextArea();
me_fiel = new Menu("文件");
mi_open = new MenuItem("打开");
mi_clo = new MenuItem("关闭");
mi_save = new MenuItem("保存");
//窗口中添加菜单栏和多行文本框
f.setMenuBar(mb);
f.add(ta);
//菜单栏添加菜单
mb.add(me_fiel);
//菜单中添加菜单项
me_fiel.add(mi_open);
me_fiel.add(mi_save);
me_fiel.add(mi_clo);
listener();
f.setVisible(true);
}
// 主窗口中添加监听器
private void listener() {
//①窗体添加监听器
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
//②“打开”菜单项添加监听器
mi_open.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//显示“打开文件对话框”
fd_open.setVisible(true);
//获取到文件路径
String pathName = fd_open.getDirectory();
//获取文件名
String fileName = fd_open.getFile();
//防止没有空指针异常
if(fileName==null&&pathName==null)
return;
//封装成File对象,读取出来并在多行文本框中打印
File dir = new File(pathName,fileName);
if(dir.toString().endsWith(".txt")||dir.toString().endsWith(".java"))
{
ta.setText("");//每次打开一个文件都要清空
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(dir));
String line = null;
while((line = br.readLine())!=null)
{
ta.append(line+"\r\n");//打印全部用append方法
}
} catch (IOException e1) {
throw new RuntimeException("文件打开失败!");
}finally{
try {
if(br!=null)
br.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
//如果不是打开的txt文件,对话框弹出
else
{
//对话框初始化
d = new Dialog(f,"错误",true);
d.setBounds(400, 200, 300, 100);
d.setLayout(new FlowLayout());
//对话框中添加组件
l = new Label();
l.setText("不是文本文件,请重新输入");
but = new Button("确定");
d.add(l);
d.add(but);
//对话框中添加监听器方法
listener_2();
//显示对话框
d.setVisible(true);
}
}
});
//③“保存”菜单项添加监听器
mi_save.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//打开系统的“文件保存对话框”
fd_save.setVisible(true);
//获取到要保存的位置
String pathName = fd_save.getDirectory();
String fileName = fd_save.getFile();
//防止空指针
if(pathName==null&&fileName==null)
return;
File f = new File(pathName,fileName);
BufferedWriter bw=null;
try {
bw = new BufferedWriter(new FileWriter(f));
bw.write(ta.getText());
bw.flush();
} catch (IOException e1) {
throw new RuntimeException("文件保存失败失败!");
}finally{
if(bw!=null)
try {
bw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
//”关闭“菜单项添加监听器
mi_clo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
}
//提示对话框上的监听机制
public void listener_2()
{
//提示窗口添加监听器
d.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e)
{
d.setVisible(false);
}
});
//确定按钮添加事件监听器
but.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent a){
d.setVisible(false);
}
});
}
public static void main(String[] args)
{
new GUIMenuDemo();
}
}