GUI 编程
Swing和AWT 是java开发GUI常用的技术,但由于外观不太漂亮, 组件数量少, 并且运行需要JRE环境(总是几百M)JRE包...), 所以不流行。 ,建议简单的学习和理解。
-
组件(JTable,JList等)很多都是MVC经典示范,学习GUI可以了解mvc架构。
-
工作时,N年前可能需要维护awt/swing写的软件 ,尽管可能性很小。
-
可以写一些自己使用用的软件,还是相当的方便。
swing是建立在awt基础上有必要学习,原因如下:
-
布局、颜色、字体、事件机制等知识的相关性...这些都是awt里的内容,但在swing也经常用在里面。
-
因为awt和swing编码差别不大,写作方法基本一致,组件使用也差不多(只需记住几个不同的地方)。
-
不同的使用场景:awt资源消耗少,运行速度快,适合嵌入式等;swing跨平台,组件丰富。
虽然现在用Java做cs很少,但是学习Java在基础上,它仍然是一个很好的资源。你可以用它来贯穿所有以前的知识,做一些小应用程序、游戏等。你可以把你的一些小想法分享成工具!
AWT
一、AWT介绍
-
AWT(Abstract Window Toolkit,抽象窗口工具)包括多种类型和接口Java Application的GUI(Graphics User Interface 编程图形用户界面)。
-
GUI各种元素(如窗口、按钮、文本框等。Java类来实现。
-
使用AWT涉及的类别一般在
Java.AWT
包括它的子包。
-
Container(容器) 和 Component(组件) 是AWT两个核心类别。
所有可以显示的图形元素都称为Component,Component代表了所有的可见的图形元素。
Component有一种特殊的图形元素叫做Container,Container(容器)在图形界面中,它可以容纳其他Component一种元素容器,Container也是一种Component,Container里面还可以容纳别的Container。
Container里面又分为Window和Pannel,Window它可以独立显示。平时看到的各种应用程序的窗口可以称为Window,Window独立显示作为应用程序窗口。
Pannel也可以容纳其他图形元素,但一般看不见Pannel,Pannel不能作为应用程序的独立窗口显示,Pannel如果你想展示,你必须把自己放进去Window只能在里面显示。Pannel典型的应用是Applet(JAVA页面小应用程序),现在基本不用了,AJAX和JAVASCRIPT完全取代了它的应用。
Window本身也可以分为Frame和Dialog,Frame是平时看到的一般窗口,Dialog需要用户进行某些操作(如点击下拉菜单项目)才能出现对话框,即Dialog。
二、组件和容器(Component & Container)
-
Java图形用户界面最基本的部分是Component,Component类及其子类的对象用于描述屏幕上的图形显示,并可以与用户互动GUI元素。例如,按钮、标签等。
-
一般的Component对象不能独立显示,必须显示 “放在” 某一个Container只能在对象中显示。
-
Container是Component的子类,Container子类对象可以 “容纳” 别的Component对象。
-
Container对象可使用add(...)向其他方法添加其他方法Component对象。
-
Container是Component因此,子类Container对象也可视为Component添加到其他对象中Container对象中。
-
常用的有两种Container:
-
Window:对象表示顶窗自由停泊。
-
Panel:其他对象可以容纳其他对象Component但不能独立存在的物体必须添加到其他物体中Container中(如Window或Applet)
-
-
2.1 Fram
-
Frame是Window的子类,由Frame或者子类创建的对象是窗体。
-
Frame常用的结构方法:
-
Frame()
-
Frame(String s):标题栏是字符串s的窗口
-
-
Frame的属性方法
-
setBounds(int x,int y,int width,int height)
:设置窗体的位置和大小。x、y左上角坐标,width、height是宽度和高度。 -
setSize(int width,int height)
:设置窗体的大小,x、y左上角坐标。 -
setLocation(int x,int y)
:设置窗体位置,x、y左上角坐标。 -
setBackground(Color c)
:设置背景颜色,参数为Color对象。 -
setVisible(boolean b)
:设置是否可见。 -
setTitle(String name)
:设置标题栏。 -
setResizable(boolean b)
:设置窗体是否能调整大小。
-
【Frame范例】
package com.kuang; import java.awt.*; //GUI编程的第一个图形界面窗口 public class TestFrame { public static void main(String[] args) { ///只是在内存中创建了一个窗口对象,还没有真正显示出来 Frame frame = new Frame("我的第一个JAVA图形界面窗口"); ///设置窗体的背景颜色 分别代表///三个参数RGB frame.setBackground(new Color(110, 217, 119)); ///设置窗体是否可见 ///要看到内存中创建的窗口对象,必须调用setVisble()方法,并且把参数true只有通过传入才能看到窗体 //如果输入的参数是false,窗体是看不见的 frame.setVisible(true); ///设置窗体的初始尺寸 frame.setSize(400,400); //设置窗体出现时的位置,如果不设置,默认显示在左上角(0,0) frame.setLocation(200,200); // 设置窗体是否可以改变大小 // 默认是true,设置为false之后,表示窗体的显示尺寸无法改变 // 这里窗户的大小设置为200X200,不能再使用鼠标拖大或者缩小 frame.setResizable(false); } }
运行结果:
发现问题:无法关闭。
解决方案:停止Java程序的运行
演示二:展示多个窗口
package com.kuang; nbsp;
import java.awt.*;
public class TestMultiFrame {
public static void main(String[] args) {
MyFrame f1 = new MyFrame(100,100,200,200,Color.blue);
MyFrame f2 = new MyFrame(300,100,200,200,Color.yellow);
MyFrame f3 = new MyFrame(100,300,200,200,Color.red);
MyFrame f4 = new MyFrame(300,300,200,200,Color.MAGENTA);
}
}
//自定义一个类MyFrame,并且从Frame类继承,这样自定义类MyFrame类就拥有了Frame类的一切属性和方法,并且MyFrame类还可以自定义属性和方法
//使用从Frame类继承而来的自定义类来创建图形窗口比直接使用Frame类来创建图形窗口要灵活,所以一般使用从Frame类继承而来的自定义类创建图形窗口界面比较好,不推荐直接使用Frame类来创建图形窗口界面
class MyFrame extends Frame{
//定义一个静态成员变量id,用来记录创建出来的窗口的数目
static int id = 0;
//自定义构成方法,在构造方法体内使用super调用父类Frame的构造方法
public MyFrame(int x,int y,int w,int h,Color color){
super("MyFrame"+(++id));
/*使用从父类Frame继承而来的方法设置窗体的相关属性*/
setBackground(color);//设置背景色
setLayout(null);//
setBounds(x,y,w,h);//设置初始位置、大小
setVisible(true);//设置是否可见
}
}
运行结果:
2.2 Panel
-
Panel对象可以堪称可以容纳Component的空间
-
Panel对象可以拥有自己的布局管理器
-
Panel类拥有从其父类继承来的方法:
-
setBounds(int x,int y,int width,int height)
:设置窗体位置和大小。x、y是左上角坐标,width、height是宽度和高度。 -
setSize(int width,int height)
:设置窗体的大小,x、y是左上角坐标。 -
setLocation(int x,int y)
:设置窗体位置,x、y是左上角坐标。 -
setBackground(Color c)
:设置背景颜色,参数为Color对象。 -
setLayout(LayoutManager mgr)
:设置布局管理器
-
-
Panel的构造方法为:
-
Panel()
:使用默认的FlowLayout类布局管理器初始化。 -
Panel(LayoutManager layout)
:使用指定的布局管理器初始化。
-
【演示】
package com.wang.gui.awt;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
* @Author wangjin
* @Date 2022/04/21 20:30
* @Description
*/
//Panel 可以看成是一个空间,但是不能单独存在
public class PanelTest {
public static void main(String[] args) {
Frame frame = new Frame();
Panel panel = new Panel();
//设置布局
frame.setLayout(null);
//设置位置和大小
frame.setBounds(300,300,500,500);
//设置背景颜色
frame.setBackground(new Color(83, 218, 84));
//panel 设置坐标,相对于Frame
panel.setBounds(50,50,400,400);
panel.setBackground(new Color(232, 78, 78));
//frame.add(panel)
frame.add(panel);
//设置可见性
frame.setVisible(true);
//监听事件解决窗口关闭问题
//监听窗口关闭事件 System.exit(0)
//适配器模式
frame.addWindowListener(new WindowAdapter() {
//窗口点击关闭时需要做的事情
@Override
public void windowClosing(WindowEvent e) {
//结束程序
System.exit(0);
}
});
}
}
结果如下:
三、布局管理器
-
Java语言中,提供了布局管理器类的对象可以管理
-
管理Component在Container中的布局,不必直接设置Component的位置和大小。
-
每个Container都有一个布局管理器对象,当容器需要对某个组件进行定位或判断其大小尺寸时,就会调用其对应的布局管理器,调用Container的setLayout()方法改变其布局管理器对象。
-
-
AWT提供了5中布局管理器:
3.1 第一种布局管理器——FlowLayout
-
FlowLayout时Panel类的默认布局管理器。
-
FlowLayout布局管理器对组件逐行定位,行内从左到右,一行排满后换行。
-
不改变组件的大小,按组件原有尺寸显示组件,可设置不同组件间距、行距以及对齐方式。
-
-
FlowLayout布局管理器默认的对齐方式是居中。
-
FlowLayout的构造方法
-
new FlowLayout(FlowLayout.RIGHT,20,40)
:右对齐,组件之间水平间距20个像素,垂直间距40个像素。 -
new FlowLayout(FlowLayout.LEFT)
:左对齐,水平和垂直间距为缺省值(5)。 -
new FlowLayout()
:使用默认的居中对齐方式,水平和垂直间距为缺省值(5)。
-
【演示】
package com.kuang;
import java.awt.*;
public class TestFlowLayout {
public static void main(String[] args) {
Frame frame = new Frame("FlowLayout");
//使用Button类创建按钮
//按钮类的其中一个构造方法:Button(String label) label为按钮显示的文本
Button button1 = new Button("button1");
Button button2 = new Button("button2");
Button button3 = new Button("button3");
// setLayout方法的定义:public void setLayout(LayoutManager mgr)
// 使用流式布局管理器 - FlowLayout
//默认水平居中
frame.setLayout(new FlowLayout());
//布局时使用FlowLayout.LEFT常量,将按钮设置为左对齐
// frame.setLayout(new FlowLayout(FlowLayout.LEFT));
//布局时使用FlowLayout.RIGHT常量,就将按钮设置为右对齐
// frame.setLayout(new FlowLayout(FlowLayout.RIGHT));
frame.setSize(200,200);
frame.add(button1); // 把创建出来的按钮放置到Frame窗体中
frame.add(button2); // 这里并没有设置按钮的大小与位置
frame.add(button3); // 设置按钮的大小与位置都是由布局管理器来做的
frame.setVisible(true);
}
}
运行结果:
3.2 第二种布局管理器——BorderLayout
-
BorderLayout时Frame类的默认布局管理器
-
BorderLayout将整个容器的布局划分成:
-
东(EAST)
-
西(WEST)
-
南(SOUTH)
-
北(NORTH)
-
中(CENTER)五个区域,组件只能被添加到指定的区域。
-
-
如不指定组件的加入部位,则默认加入到CENTER区。
-
每个区域只能加入一个组件,如加入多个,则先前加入的会被覆盖。
-
BorderLayout型布局管理器尺寸缩放原则:
-
北、南两个区域在水平方向缩放。
-
东、西两个区域在垂直方向缩放。
-
中部可在两个方向上缩放。
-
package com.kuang;
import java.awt.*;
public class BorderLayoutTest{
public static void main(String[] args) {
Frame frame = new Frame("BorderLayoutTest");
Button east = new Button("East");
Button west = new Button("West");
Button south = new Button("South");
Button north = new Button("North");
Button center = new Button("Center");
//把按钮放置到Frame窗体时按照东西南北中五个方向排列好,推荐使用这种方式去排列窗体元素
//这样容易检查出错误 因为这样写如果写错了编译器会提示出错
frame.add(east,BorderLayout.EAST);
frame.add(west,BorderLayout.WEST);
frame.add(south,BorderLayout.SOUTH);
frame.add(north,BorderLayout.NORTH);
frame.add(center,BorderLayout.CENTER);
//也可以使用这样的方式排列按钮,在把按钮放置到Frame窗体时使用方向定位的字符串指定按钮的放置位置
//这种使用方向定位的字符串指定按钮的放置方式不推荐使用 一旦写错了方向字符串就不好检查出来
//因为即使是写错了仍然可以编译通过
/*
frame.add(east,"EAST");
frame.add(west,"West");
frame.add(south,"South");
frame.add(north,"North");
frame.add(center,"Center");
*/
frame.setSize(200,200);
frame.setVisible(true);
}
}
运行结果:
3.3 第三种布局管理器——GridLayout(表格布局管理器)
-
GridLayout型布局管理器将空间划分成规则的矩形网格,每个单元格区域大小相等。组件被添加到每个单元格中,先从左到右填满一行后换行,再从上到下。
-
在GridLayout构造方法中指定分割的行数和列数:
-
如:
GridLayout(3,4);
-
【演示】
package com.kuang;
import java.awt.*;
public class GridLayoutTest {
public static void main(String[] args) {
Frame frame = new Frame("GridLayoutTest");
Button btn1 = new Button("btn1");
Button btn2 = new Button("btn2");
Button btn3 = new Button("btn3");
Button btn4 = new Button("btn4");
Button btn5 = new Button("btn5");
Button btn6 = new Button("bnt6");
// 把布局划分成3行2列的表格布局形式
frame.setLayout(new GridLayout(3,2));
frame.add(btn1);
frame.add(btn2);
frame.add(btn3);
frame.add(btn4);
frame.add(btn5);
frame.add(btn6);
// Frame.pack()是JAVA语言的一个函数
// 这个函数的作用就是根据窗口里面的布局及组件的preferredSize来确定frame的最佳大小。
frame.pack();
frame.setVisible(true);
}
}
运行结果:
3.4 布局练习
这几种布局管理器可以设置在Frame里面,也可以设置在Panel里面,而Panel本身也可以加入到Frame里面,因此通过Frame与Panel的嵌套就可以实现比较复杂的布局;
【演示】
package com.kuang;
import java.awt.*;
public class TestTenButtons {
public static void main(String[] args) {
//对显示窗体进行设置
Frame frame = new Frame("布局管理器的嵌套使用");
//把整个窗体分成2行1列的表格布局
frame.setLayout(new GridLayout(2,1));
frame.setLocation(300,400);
frame.setSize(400,300);
frame.setVisible(true);
frame.setBackground(new Color(204,204,255));
//对Panel进行布局的设置
Panel p1 = new Panel(new BorderLayout());
//p2使用2行1列的表格布局
Panel p2 = new Panel(new GridLayout(2,1));
Panel p3 = new Panel(new BorderLayout());
//p4使用2行2列的表格布局
Panel p4 = new Panel(new GridLayout(2,2));
//这里主要是把按钮元素加入到Panel里面
p1.add(new Button("East-1"),BorderLayout.EAST);
p1.add(new Button("West-1"),BorderLayout.WEST);
p2.add(new Button("p2-btn-1"));
p2.add(new Button("p2-btn-2"));
//p1里面嵌套p2,把p2里面的按钮作为p的中间部分装入到p1里面
//把p2作为元素加入到p1里面
p1.add(p2,BorderLayout.CENTER);
p3.add(new Button("East-2"),BorderLayout.EAST);
p3.add(new Button("West-2"),BorderLayout.WEST);
for(int i=0;i<4;i++){
p4.add(new Button("p4-btn-"+i));
}
//p3里面嵌套p4,把p4里面的按钮作为p的中间部分装入到p3里面
p3.add(p4,BorderLayout.CENTER);
//把Panel装入Frame里面,以便于在Frame窗体中显示出来
frame.add(p1);
frame.add(p3);
//事件监听,实现窗口关闭
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
运行结果 :
四、布局管理器总结
-
Frame是一个顶级窗口,Frame的默认布局管理器为BorderLayout。
-
Panel无法单独显示,必须添加到某个容器中。
-
Panel的默认布局管理器为FlowLayout
-
-
布局管理器:
-
FlowLayout -- 流式布局管理器
-
BorderLayout -- 东西南北中布局管理器
-
GridLayout -- 表格布局管理器
-
-
当把Panel作为一个组件添加到某个容器中后,该Panel仍然可以有自己的布局管理器。
-
使用布局管理器时,布局管理器负责各个组件的大小和位置,因此用户无法在这种情况下设置组件的大小和位置属性,如果试图使用Java语言的
setLocation()
、setSize()
、setBounds()
等方法,则会被布局管理器覆盖。 -
如果用户确实需要亲自设置组件大小或位置,则应取消该容器的布局管理器,方法为:
-
setLayout(null);
-
五、事件监听
【测试代码一】
package com.kuang;
import java.awt.*;
import java.awt.event.*;
public class ActionEventTest{
public static void main(String[] args) {
Frame frame = new Frame("TestActionEvent");
Button button = new Button("Press Me");
// 创建一个监听对象
MyActionListener listener = new MyActionListener();
// 把监听加入到按钮里面,监听按钮的动作,
// 当按钮触发打击事件时,就会返回一个监听对象e,然后会自动执行actionPerformed方法
button.addActionListener(listener);
frame.add(button, BorderLayout.CENTER);
frame.pack();
addWindowClosingEvent(frame);
frame.setVisible(true);
}
//点击窗体上的关闭按钮关闭窗体
private static void addWindowClosingEvent(Frame frame){
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
// 自定义Monitor(监听)类实现事件监听接口ActionListener
// 一个类要想成为监听类,那么必须实现ActionListener接口
class MyActionListener implements ActionListener{
//重写ActionListener接口里面的actionPerformed(ActionEvent e)方法
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("A Button has been Pressed");
}
}
点击窗口Press Me按钮,触发事件,点击窗口关闭按钮可以实现窗口的关闭。
【测试代码二】
多个按钮共用同一个监听事件
package com.kuang;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ActionEventTest02 {
public static void main(String[] args) {
Frame frame = new Frame("ActionEventTest");
Button btn1 = new Button("start");
Button btn2 = new Button("stop");
//创建监听对象
MyMonitor monitor = new MyMonitor();
//一个监听对象同时监听两个按钮的动作
btn1.addActionListener(monitor);
btn2.addActionListener(monitor);
//可以显示设置btn2的执行单击命令后的返回信息,如果不显示定义,则会返回默认信息
//可以多个按钮只写一个事件监听类
btn2.setActionCommand("GameOver");
frame.add(btn1,BorderLayout.NORTH);
frame.add(btn2,BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
class MyMonitor implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
//使用返回的监听对象e调用getActionCommand()方法获取两个按钮执行单击命令后的返回信息,根据返回信息的不同区分当前操作的是哪一个按钮
//btn1没有使用setActionCommand()方法设置,则btn1返回的信息就是按钮上显示的文本
System.out.println("a button has been pressed,"+"the relative info is:\n"
+ e.getActionCommand());
}
}
分别点击start按钮和stop按钮出发监听事件,点击关闭窗口按钮实现窗口的关闭。
六、TexField事件监听
-
TexField对象可能发生Action(光标在文本框内敲回车)事件。与该事件对应的事件类是
java.awt.event.ActionEvent
-
用来处理 ActionEvent 事件是实现了
java.awt.event.ActionListener
接口的类的对象。ActionListener接口定义有方法:-
public void actionPerformed(ActionEvent e)
-
-
实现该接口的类要在该方法中添加处理该事件(Action)的语句。
-
实现 addActionListener(ActionListener I)方法为TexField对象注册一个ActionListener对象,当TexField对象发生Action事件时,会生成一个ActionEvent对象,该对象作为参数传递给ActionListener对象的actionPerformed方法在方法中可以获取该对象的信息,并做相应的处理。
package com.kuang;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TextFieldTest{
public static void main(String[] args) {
new MyFrameTextField();
}
}
class MyFrameTextField extends Frame{
MyFrameTextField(){
TextField textField = new TextField();
add(textField);
textField.addActionListener(new MyMonitor2());
//setEchoChar()方法:设置文本框输入时显示的字符,这里设置为*,
//这样输入任何内容就都以*显示出来,不过打印出来时依然可以看到输入的内容
textField.setEchoChar('*');
setVisible(true);
pack();
}
}
class MyMonitor2 implements ActionListener{
//接口里面的所有方法都是public(公共的)
//从API文档复制void actionPerformed(ActionEvent e)时,要在void前面加上public
@Override
public void actionPerformed(ActionEvent e) {
//事件的相关信息都封装在了对象e里面,通过对象e的相关方法可以获取事件的相关信息
//getSource()方法:拿到事件源,获得一些资源,返回一个对象
//注意:拿到这个事件源时,把它当作TextField的父类来对待
//getSource()方法的定义是:“public Object getSource()”返回值是一个Object对象
//所以要强制转换成TextField类型的对象
//在一个类里面想访问另一个类的事件源对象可以通过getSource()方法
TextField textField = (TextField) e.getSource();
// textField.getText()是取得文本框里面的内容
System.out.println(textField.getText());
// 把文本框里面的内容清空
textField.setText("");
}
}
【使用TextField类实现简单的计算器】
package com.kuang2;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestMath {
public static void main(String[] args) {
new Calculator();
}
}
//主要是完成计算器元素的布局
class Calculator extends Frame{
Calculator(){
//创建3个文本框,并指定其初始大小分别为10个字符和15个字符的大小
//使用的是TextField类的另外一种构造方法:public TextField(int columns)
TextField num1 = new TextField(10);
TextField num2 = new TextField(10);
TextField num3 = new TextField(15);
//创建等号按钮
Button btnEqual = new Button("=");
//给等号按钮加上监听,让点击按钮后有响应事件发生
btnEqual.addActionListener(
new MyMonitor(num1, num2, num3)
);
//“+”是一个静态文本,所以使用Label类创建一个静态文本对象
Label lblPlus = new Label("+");
//把Frame默认的BorderLayout布局改成FlowLayout布局
setLayout(new FlowLayout());
add(num1);
add(lblPlus);
add(num2);
add(btnEqual);
add(num3);
pack();
setVisible(true);
}
}
class MyMonitor implements ActionListener{
//为了使对按钮的监听能够对文本框也起作用,在自定义类MyMonitor里面定义三个TextField类型的对象 num1,num2,num3,
//并且定义了MyMonitor类的一个构造方法,构造方法带有三个TextField类型的参数,用于接收从TFFrame类传递过来的三个TextField类型的参数
//然后把接收到的三个TextField类型的参数赋值给在本类中声明的三个TextField类型的参数num1,num2,num3,再在actionPerformed()方法里面处理num1,num2,num3
TextField num1, num2, num3;
public MyMonitor(TextField num1, TextField num2, TextField num3) {
this.num1 = num1;
this.num2 = num2;
this.num3 = num3;
}
//事件的相关信息都封装在了对象e里面,通过对象e的相关方法就可以获取事件的相关信息
@Override
public void actionPerformed(ActionEvent e) {
// num对象调用getText()方法取得显示的文本字符串
int n1 = Integer.parseInt(num1.getText());
int n2 = Integer.parseInt(num2.getText());
//num3对象调用setText()方法设置自己的显示文本
//字符串与任意类型的数据使用“+”连接时得到的一定是字符串,这里使用一个空字符串与int类型的数连接,就可以直接把(n1+n2)得到的int类型的数隐式地转换成字符串了,这是一种把别的基本数据类型转换成字符串的一个小技巧。
//也可使用“String.valueOf((n1+n2))”把(n1+n2)的和转换成字符串
num3.setText("" + (n1 + n2));
//num3.setText(String.valueOf((n1+n2)));
//计算结束后清空num1,num2文本框里面的内容
num1.setText("");
num2.setText("");
}
}
【JAVA里面的经典用法:在一个类里面持有另外一个类的引用】
完全改造成面向对象写法
package com.kuang2;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestMath1 {
public static void main(String[] args) {
new Calculator().launchFrame();
}
}
//做好计算器的窗体界面
class Calculator extends Frame {
//把设计计算器窗体的代码封装成一个方法
TextField num1, num2, num3;
public void launchFrame() {
num1 = new TextField(10);
num2 = new TextField(10);
num3 = new TextField(15);
Label lblPlus = new Label("+");
Button btnEqual = new Button("=");
btnEqual.addActionListener(new MyMonitorbtnEqual(this));
setLayout(new FlowLayout());
add(num1);
add(lblPlus);
add(num2);
add(btnEqual);
add(num3);
pack();
setVisible(true);
}
}
//通过取得Calculator2类的引用,然后使用这个引用去访问Calculator2类里面的成员变量
//这种做法比上一种直接去访问Calculator2类里面的成员变量要好得多
//因为现在不需要知道 Calculator2类里面有哪些成员变量了,现在要访问Calculator2类里面的成员变量,直接使用 Calculator2类对象的引用去访问即可
//这个Calculator2类的对象好比是一个大管家, 而我告诉大管家,我要访问Calculator2类里面的那些成员变量,大管家的引用就会去帮我找,不再需要我自己去找了。
//这种在一个类里面持有另一个类的引用的用法是一种非常典型的用法,使用获取到的引用就可以在一个类里面访问另一个类的所有成员了
class MyMonitorbtnEqual implements ActionListener {
Calculator2 calculator2 = null;
public MyMonitorbtnEqual(Calculator2 calculator2) {
this.calculator2 = calculator2;
}
@Override
public void actionPerformed(ActionEvent e) {
int n1 = Integer.parseInt(calculator2.num1.getText());
int n2 = Integer.parseInt(calculator2.num2.getText());
calculator2.num3.setText("" + (n1 + n2));
calculator2.num1.setText("");
calculator2.num2.setText("");
}
}
结果:
七、内部类
-
好处:
-
可以方便的访问包装类的成员
-
可以更清楚的组织逻辑,防止不应该被其他类访问的类进行访问
-
-
何时使用:
-
该类不允许或不需要其它类进行访问时
-
【内部类的使用范例】
package com.kuang2;
import java.awt.*;
import java.awt.event.*;
public class TestMath3 {
public static void main(String args[]) {
new MyMathFrame().launchFrame();
}
}
class MyMathFrame extends Frame {
TextField num1, num2, num3;
public void launchFrame() {
num1 = new TextField(10);
num2 = new TextField(15);
num3 = new TextField(15);
Label lblPlus = new Label("+");
Button btnEqual = new Button("=");
btnEqual.addActionListener(new MyMonitor());
setLayout(new FlowLayout());
add(num1);
add(lblPlus);
add(num2);
add(btnEqual);
add(num3);
pack();
setVisible(true);
}
/*
* 这个MyMonitor类是内部类,它在MyFrame类里面定义MyFrame类称为MyMonitor类的包装类
*/
/*
* 使用内部类的好处:
* 第一个巨大的好处就是可以畅通无阻地访问外部类(即内部类的包装类)的所有成员变量和方法
* 如:这里在MyFrame类(外部类)定义的三个成员变量num1,num2,num3,在MyMonitor(内部类)里面就可以直接访问
* 这相当于在创建外部类对象时内部类对象默认就拥有了一个外部类对象的引用
*/
private class MyMonitor implements ActionListener {
public void actionPerformed(ActionEvent e) {
int n1 = Integer.parseInt(num1.getText());
int n2 = Integer.parseInt(num2.getText());
num3.setText("" + (n1 + n2));
num1.setText("");
num2.setText("");
}
}
}
内部类带来的巨大好处是:
-
可以很方便地访问外部类定义的成员变量和方法
-
当某一个类不需要其他类访问的时候就把这个类声明为内部类。
八、Graphics类
每个Component都有一个paint(Graphics g)用于实现绘图目的,每次重画该Component时都自动调用paint方法。
Graphics类中提供了许多绘图方法,如:
【测试代码】
package com.kuang3;
import java.awt.*;
public class TestPaint {
public static void main(String[] args) {
new MyPaint().launchFrame();
//在main()方法里面并没有显示调用paint(Graphics g)方法,可是当创建出Frame窗体后却可以看到Frame窗体上画出了圆和矩形,这是因为paint()方法是一个比较特殊的方法
//在创建Frame窗体时会自动隐式调用,当我们把Frame窗体最小化再次打开时,会再次调用paint()方法重新把圆和矩形在Frame窗体上画出来,即每次需要重画Frame窗体的时候就会自动调用paint()方法
}
}
class MyPaint extends Frame{
public void launchFrame(){
setBounds(200,200,600,500);
setVisible(true);
}
public void paint(Graphics g){
//paint(Graphics g)方法有一个Graphics类型的参数g
//可以把这个g当作是一个画家,这个画家手里拿着一只画笔,我们通过设置画笔的颜色与形状来画出我们想要的各种各样的图像
/*设置画笔的颜色*/
g.setColor(Color.red);
g.drawOval(100, 100, 100, 100);//画一个红色空心圆
g.fillOval(100, 300, 100, 100);//画一个红色实心圆
g.setColor(Color.green);
g.fillRect(300,300,100,100);//画一个绿色实心圆
//这下面的两行代码是为了写程序的良好编程习惯而写的
//前面设置了画笔的颜色,现在就应该把画笔的初始颜色恢复过来
//就相当于是画家用完画笔之后把画笔上的颜色清理掉一样
Color c = g.getColor();
g.setColor(c);
}
}
九、鼠标事件适配器
-
抽象类
java.awt.event.MouseAdapter
实现了MouseListener接口,可以使用其子类作为MouseEvent的监听器,只要重写其相应的方法即可。
-
对于其他的监听器,也有对应的适配器。
-
适用适配器可以避免监听器定义没有必要的空方法。
【测试代码:实现鼠标画点】
package com.wang.gui.awt;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Iterator;
//鼠标监听事件
public class MouseListenerTest {
public static void main(String[] args) {
new MyFrame1("画图");
}
}
//自己的类
class MyFrame1 extends Frame {
//画画需要画笔,需要监听鼠标当前的位置,需要集合来存储这个点
ArrayList points;
public MyFrame1(String title) {
super(title);
setBounds(200, 200, 400, 300);
//存鼠标点击的点
points = new ArrayList<>();
setVisible(true);
//鼠标监听器,针对这个窗口
this.addMouseListener(new MyMouseListener());
}
@Override
public void paint(Graphics g) {
//画画,监听鼠标事件
Iterator iterator = points.iterator();
while (iterator.hasNext()) {
Point point = (Point) iterator.next();
g.setColor(Color.BLUE);
g.fillOval(point.x, point.y, 10, 10);
}
}
//添加一个点到界面上
public void addPoint(Point point) {
points.add(point);
}
//适配器模式
private class MyMouseListener extends MouseAdapter {
//鼠标三种情况:按下、弹起、按住不放
@Override
public void mousePressed(MouseEvent e) {
MyFrame1 myFrame1 = (MyFrame1) e.getSource();
//鼠标点击,就会在界面上出现一个点
//这个点就是鼠标的点
myFrame1.addPoint(new Point(e.getX(), e.getY()));
//每次点击鼠标都需要重画一遍,执行一次刷新一遍
myFrame1.repaint();
}
}
}
分析:
十、窗口事件
-
窗口事件所对应的事件类为WindowEvent,所对应的事件监听接口为WindowListener。
-
WindowListener定义的方法有:
-
public void windowOpened(WindowEvent e)
-
public void windowClosing(WindowEvent e)
-
public void windowClosed(WindowEvent e)
-
public void windowIconified(WindowEvent e)
-
public void windowDeiconified(WindowEvent e)
-
public void windowActivated(WindowEvent e)
-
public void windowDeactivated(WindowEvent e)
-
-
与WindowListener对应的适配器为WindowAdapter。
package com.kuang3;
import java.awt.*;
import java.awt.event.*;
public class TestWindowClose{
public static void main(String args[]){
new WindowFrame("关闭WindowFrame");
}
}
class WindowFrame extends Frame{
public WindowFrame(String s){
super(s);
setBounds(200,200,400,300);
setLayout(null);
setBackground(new Color(204,204,255));
setVisible(true);
this.addWindowListener(new WindowMonitor());
/*监听本窗体的动作,把所有的动作信息封装成一个对象传递到监听类里面*/
this.addWindowListener(
/*在一个方法里面定义一个类,这个类称为局部类,也叫匿名的内部类,这里的{……代码……}里面的代码很像一个类的类体,只不过这个类没有名字,所以叫匿名类。
在这里是把这个匿名类当成WindowAdapter类来使用,这样写的本质意义是相当于这个匿名类从WindowAdapter类继承,现在new了一个匿名类的对象出来,然后把这个对象当成WindowAdapter来使用,这个匿名类出了()就没有人认识了*/
new WindowAdapter(){
public void windowClosing(WindowEvent e){
setVisible(false);
System.exit(-1);
}
}
);
}
/*这里也是将监听类定义为内部类*/
class WindowMonitor extends WindowAdapter{
/*WindowAdapter(Window适配器)类实现了WindowListener监听接口,重写了WindowListener接口里面的所有方法。
如果直接使用自定义WindowMonitor类去实现WindowListener接口,就得要重写WindowListener接口里面的所有方法,但现在只需要用到这些方法里面的其中一个方法,所以采用继承实现WindowListener监听接口的一个子类并重写这个子类里面需要用到的方法即可。
这种做法比直接实现WindowListener监听接口要重写很多个用不到的方法要简洁方便得多。
*/
/*重写需要用到的windowClosing(WindowEvent e)方法*/
public void windowClosing(WindowEvent e){
setVisible(false);/*将窗体设置为不显示,即可实现窗体关闭*/
System.exit(0);/*正常退出*/
}
}
}
十一、键盘响应事件
【键盘响应事件——KeyEvent】
package com.kuang3;
import java.awt.*;
import java.awt.event.*;
public class TestKeyEvent{
public static void main(String args[]){
new KeyFrame("键盘响应事件");
}
}
class KeyFrame extends Frame{
public KeyFrame(String s){
super(s);
setBounds(200,200,400,300);
setLayout(null);
setVisible(true);
addKeyListener(new KeyMonitor());
}
/*把自定义的键盘监听类定义为内部类,这个监听类从键盘适配器KeyAdapter类继承。
从KeyAdapter类继承是为了简洁方便,只需要重写用到的方法即可。这种做法比直接实现KeyListener接口要简单方便,如果直接实现KeyListener接口,就要把KeyListener接口里面的所有方法重写一遍。但真正用到的只有一个方法,这样重写其他的方法但又用不到难免会做无用功*/
class KeyMonitor extends KeyAdapter{
public void keyPressed(KeyEvent e){
int keycode = e.getKeyCode();
/*使用getKeyCode()方法获取按键的虚拟码*/
/*如果获取到的键的虚拟码等于up键的虚拟码,则表示当前按下的键是up键。
KeyEvent.VK_UP表示取得up键的虚拟码
键盘中的每一个键都对应一个虚拟码,这些虚拟码在KeyEvent类里被定义为静态常量,所以可以使用“类名.静态常量名”的形式访问得到这些静态常量*/
if(keycode == KeyEvent.VK_UP){
System.out.println("你按的是up键");
}
}
}
}
/*
键盘的处理事件是这样的:每一个键都对应一个虚拟码,当按下某一个键时,系统就会去找这个键对应的虚拟码,以此来确定当前按下的是哪个键
*/
Swing
Swing是GUI(图形用户界面)开发工具包,想深入学习的可查阅有关资料或图书,比如《Java Swing图形界面开发与案例详解》——清华大学出版社。
早期的AWT(抽象窗口工具包)组件开发的图形用户界面,要依赖本地系统,当把AWT组件开发的应用程序移植到其他平台的系统上运行时,不能保证其外观风格,因此AWT是依赖于本地系统平台的。而使用Swing开发的Java应用程序,其界面是不受本地系统平台限制的,即Swing开发的Java应用程序移植到其他系统平台上时,其界面外观是不会改变的。
但要注意的是,虽然Swing提供的组件可以方便开发Java应用程序,但是Swing并不能取代AWT,在开发Swing程序时通常要借助与AWT的一些对象来共同完成应用程序的设计。
一、常用窗体
Swing窗体是Swing的一个组件,同时也是创建图形化用户界面的容器,可以将其它组件放置在窗体容器中。
1.1 JFrame框架窗体
JFrame窗体是一个容器,在Swing开发中经常要用到,它是Swing程序中各个组件的载体。
语法格式如下:
JFrame jf = new JFrame(title);
当然,在开发中更常用的方式是通过继承java.swing.JFrame
类创建一个窗体,可通过this关键字调用其方法。
在JFrame对象创建完成后,需要调用getContentPane()方法
将窗体转换为容器,然后在容器中添加组件或设置布局管理器,通常这个容器用来包含和显示组件。如果需要将组件添加至容器,可以使用来自Container类的add()方法
进行设置。
【下面举一个JFrame窗体的例子】
package com.kuang4;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class JFrameDemo {
public void CreateJFrame() {
// 实例化一个JFrame对象
JFrame jf = new JFrame("这是一个JFrame窗体");
// 设置窗体可视
jf.setVisible(true);
// 设置窗体大小
jf.setSize(100,100,500, 600);
// 设置窗体关闭方式
jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JFrameDemo().CreateJFrame(); // 调用CreateJFrame()方法
}
}
结果:
这就是一个500*600的窗体,用的是setSize()方法;
标题为“这是一个JFrame窗体”,在实例化对象时就可以定义;
窗体关闭方式见窗体右上角为“EXIT_ON_CLOSE”;
窗体可视setVisible()方法中的参数为“false”或不写setVisible()方法时,此窗体不可见。
-
DO_NOTHING_ON_CLOSE
:什么也不做就将窗体关闭;
-
DISPOSE_ON_CLOSE”
:任何注册监听程序对象后会自动隐藏并释放窗体;
-
HIDE_ON_CLOSE”
: 隐藏窗口的默认窗口关闭;
-
EXIT_ON_CLOSE
:退出应用程序默认窗口关闭。
【下面再举一个用继承JFrame的方式编写的代码,并加入Container容器及JLabel标签(后面会提到),来看一下具体的流程。】
package com.kuang4;
import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
public class JFrameDemo2 extends JFrame{
public void init() {
// 可视化
this.setVisible(true);
// 大小
this.setSize(500, 350);
// 标题
this.setTitle("王车车");
// 关闭方式
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// 创建一个JLabel标签
JLabel jl = new JLabel("欢迎来到我的世界!");
// 使标签文字居中
jl.setHorizontalAlignment(SwingConstants.CENTER);
// 获取一个容器
Container container = this.getContentPane();
// 将标签添加至容器
container.add(jl);
// 设置容器背景颜色
container.setBackground(Color.YELLOW);
}
public static void main(String[] args) {
new JFrameDemo2().init();
}
}
运行结果:
这里继承了JFrame类,所以方法中实现时用this关键字即可(或直接实现,不加this)。
1.2 JDialog弹窗
JDialog窗体是Swing组件中的对话框,继承了AWT组件中的java.awt.Dialog
类。功能是从一个窗体中弹出另一个窗体。
【下面来看一个实例】
package com.kuang4;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
// 继承JDialog类
public class JDialogDemo extends JDialog {
// 实例化一个JDialog类对象,指定其父窗体、窗口标题和类型
public JDialogDemo() {
super(new MyJFrame(), "这是一个JDialog窗体", true);
Container container = this.getContentPane();
container.add(new JLabel("王车车爱学习!"));
this.setSize(500, 350);
}
public static void main(String[] args) {
new JDialogDemo();
}
}
// 下面这部分内容包含监听器,可自行查阅资料
class MyJFrame extends JFrame {
public MyJFrame() {
this.setVisible(true);
this.setSize(700, 500);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
Container container = this.getContentPane();
container.setLayout(null);
JButton jb = new JButton("点击弹出对话框"); // 创建按钮
jb.setBounds(30, 30, 200, 50); // 按钮位置及大小
jb.addActionListener(new ActionListener() { // 监听器,用于监听点击事件
@Override
public void actionPerformed(ActionEvent e) {
new JDialogDemo().setVisible(true);
}
});
container.add(jb);
}
}
当点击按钮时,触发点击事件,创建一个JDialog的实例化对象,弹出一个窗口。
这里出现了许多之前学过的知识,比如super关键字,相当于使用了JDialog(Frame f, String title,boolean model)形式的构造方法;监听器的实现就是一个匿名内部类,之前也提到过。
二、标签组件
在Swing中显示文本或提示信息的方法是使用标签,它支持文本字符串和图标。上面我们提到的JLabel就是这里的内容。
2.1 标签
标签由JLabel类定义,可以显示一行只读文本、一个图像或带图像的文本。
JLabel类提供了许多构造方法,可查看API选择需要的使用,如显示只有文本的标签、只有图标的标签或包含文本与图标的标签等。因为上面已经出现过了,这里就不再举例了。常用语法格式如下,创建的是一个不带图标和文本的JLabel对象:
JLabel jl = new JLabel();
2.2 图标
Swing中的图标可以放置在按钮、标签等组件上,用于描述组件的用途。图标可以用Java支持的图片文件类型进行创建,也可以使用java.awt.Graphics类提供的功能方法来创建。