Swing 组件都采用 MVC(Model-View-Controller,即模型-视图-控制器)的设计,实现 GUI 组件的显示逻辑和数据逻辑的分离,从而允许程序员自定义 Render 来改变 GUI 组件的显示外观,以提供更多的灵活性。
Swing 围绕 JComponent 组件构建,JComponent 则由 AWT 的容器类扩展而来。Swing 组织结构如图 1 所示。
public static void main(String[] args) {
//创建窗口对象
Frame frame=new Frame("window测试");
// 创建窗口大小,位置
frame.setLocation(100,100);
frame.setSize(500,300);
//设置窗口对象可见
frame.setVisible(true);
}
}
点击IDEA中的RUN,你可以得到一下窗口界面。
但是也有个问题我们需要手动在RUN中点掉暂停键,我们没办法关闭右上角的'X'(取消窗口) 。
Panel是面板,是一个很少直接使用的基本组件,或者继承重写,或者用来组织其他组件。
panel和Jpanel都为中间层容器,可显示文字、图像、绘制图形,主要功能是在GUI中组织其他组件。
public static void main(String[] args) {
//1.创建一个window对象,因为,panel以及其他的容器,都不能独立存在,
//必须依靠window存在
Frame frame=new Frame();
//2.创建一个Panel对象
Panel p=new Panel();
//3.创建一个文本框和按钮,并且放入到Panel容器中
p.add(new TextField("测试文本"));
p.add(new Button("这是一个测试按钮"));
//4.把Pannel放入window中
frame.add(p);
//5.设置window的位置以及大小
frame.setBounds(100,100,500,300);
//6.设置window可见
frame.setVisible(true);
}
1.点击运行却又出现按钮中乱码现象。
2. 此时由于我们用的是UTF-8,故需要不让乱码出现,则点击下方图片选项
3. 点进去之后,直接Alt+V,在出现的VM options的方框里面添加这么一句话 :
4.-Dfile.encoding=gbk
5.然后再次运行即可。
public static void main(String[] args) {
Frame frame=new Frame();
//1.创建一个ScrollPane对象
ScrollPane sp=new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
//--->默认创建一个滚动条ScrollPane.SCROLLBARS_ALWAYS
//2.往ScrollPane中添加内容
sp.add(new TextField("测试文本"));
sp.add(new Button("测试按钮"));
//3.把ScrollPane添加到Frame中
frame.add(sp);
frame.setBounds(100,100,500,300);
frame.setVisible(true);
}
1.当然你运行的时候还是会有乱码出现,但问题不大,直接参考Study02蓝色字体。
public static void main(String[] args) {
Frame frame = new Frame("测试FlowLayout");
//1.通过setLayout方法设置容器的布局管理器
//1)向左边对齐
frame.setLayout(new FlowLayout(FlowLayout.LEFT,20,20));//20为像素单位
//2)向中间对齐
frame.setLayout(new FlowLayout(FlowLayout.CENTER,20,20));//20为像素单位
//3)向右边对齐
frame.setLayout(new FlowLayout(FlowLayout.RIGHT, 40, 20));//20为像素单位
//2.添加多个按钮到frame中
for (int i = 0; i <= 100; i++) {
frame.add(new Button("按钮" + i));
}
//3.设置最佳大小,pack方法
frame.pack();
frame.setVisible(true);
}
输出样例:向右的按钮输出
public static void main(String[] args) {
Frame frame=new Frame("这里测试BorderLayout");
//1.给frame设置BorderLayout布局管理器
frame.setLayout(new BorderLayout(30,10));
//2.往frame的指定区域添加组件
frame.add(new Button("北侧按钮"),BorderLayout.NORTH);
frame.add(new Button("南侧按钮"),BorderLayout.SOUTH);
frame.add(new Button("东侧按钮"),BorderLayout.EAST);
frame.add(new Button("西侧按钮"),BorderLayout.WEST);
frame.add(new Button("中间按钮"),BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
输出样例:
public static void main(String[] args) {
Frame frame=new Frame("这里测试BorderLayout");
//1.给frame设置BorderLayout布局管理器
frame.setLayout(new BorderLayout(30,10));
//2.往frame的指定区域添加组件
frame.add(new Button("北侧按钮"),BorderLayout.NORTH);
frame.add(new Button("南侧按钮"),BorderLayout.SOUTH);
frame.add(new Button("中间按钮"),BorderLayout.CENTER);
frame.add(new TextField("测试文本"));
frame.pack();
frame.setVisible(true);
}
输出样例:
1.但是这样的话会将中间按钮给覆盖掉了,那么如何将按钮与测试文本共同放在中间且不互相干扰呢?
public static void main(String[] args) {
Frame frame=new Frame("这里测试BorderLayout");
//1.给frame设置BorderLayout布局管理器
frame.setLayout(new BorderLayout(30,10));
//2.往frame的指定区域添加组件
frame.add(new Button("北侧按钮"),BorderLayout.NORTH);
frame.add(new Button("南侧按钮"),BorderLayout.SOUTH);
Panel p=new Panel();
p.add(new Button("中间按钮"));
p.add(new TextField("测试文本框"));
frame.add(p);
frame.pack();
frame.setVisible(true);
}
输出样例:
如何做一个简单的计算器界面?
public static void main(String[] args) {
Frame frame = new Frame();
//1、创建一个Panel对象,里面存放一个TextFiled组件
Panel p=new Panel();
p.add(new TextField(30));
//2、把当前这个Panel添加到frame的北边区域
frame.add(p,BorderLayout.NORTH);
//3、创建一个Panel对象,并且设置他的布局管理器为GridLayout
Panel p2=new Panel();
p2.setLayout(new GridLayout(5,10,3,3));
//4.往Panel中添加内容
for(int i=0;i<10;i++){
p2.add(new Button(i+""));
}
p2.add(new Button("+"));
p2.add(new Button("-"));
p2.add(new Button("*"));
p2.add(new Button("/"));
p2.add(new Button("."));
//5.把当前Panel添加到frame中
frame.add(p2);
frame.pack();
frame.setVisible(true);
}
输出样例:
什么是CardLayout,CardLayout布局管理器以时间而非空间来管理它的组件,它将容器的所有组件看成一叠卡片(每个卡片其实就是一个组件),每次只有最上面的Companent才可见。就好像一副扑克牌,它们叠在一起,每次只有最上面的一张扑克牌可见。
方法名称 | 方法功能 |
CardLayout() | 创建默认的CardLayout布局管理器 |
CardLayout(int hgap,int vgap) | 通过指定卡片与容器左右边界的间距,上下边界间距来决定CardLayout的布局管理器 |
first(Container target) | 显示target容器中的第一张卡片 |
last(Container target) | 显示target容器中的最后一张卡片 |
previou(Container target) | 显示target容器中前一张卡片 |
next(Container target) | 显示target容器中的后一张卡片 |
show(Container target,String name) | 显示target容器中指定名字的卡片 |
public static void main(String[] args) {
Frame frame=new Frame("测试CardLayout");
//1、创建一个Panel容器,用来存储多张卡片
Panel p1=new Panel();
//2、创建一个CardLayout对象,并且把该对象设置给之前创建的Panel容器中
CardLayout cardLayout=new CardLayout();
p1.setLayout(cardLayout);
//3、往Panel中储存多个组织
String[] name={"第一张","第二张","第三张","第四张","第五张"};
for(int i=0;i
样例输出:有点类似与windows中的图片浏览软件,可以来回切换上下张图片。
方法名称 | 方法功能 |
BoxLayout(Container target,int axis) | 指定创建基于target容器的BoxLayout布局管理器,该布局管理器里的组件按照axis方向排列。其中axis有BoxLayout.X_AXIS(横向)和BoxLayout.Y_AXIS(纵向)两个方向 |
public static void main(String[] args) {
Frame frame=new Frame();
//1、基于frame容器,创建一个BoxLayout对象,并且,该对象存放的组件是垂直存放
//BoxLayout boxLayout = new BoxLayout(frame, BoxLayout.Y_AXIS);
//------>基于frame容器,创建一个BoxLayout对象,并且,该对象存放的组件是水平存放
BoxLayout boxLayout = new BoxLayout(frame, BoxLayout.X_AXIS);
//2、把BoxLayout对象设置给Frame
frame.setLayout(boxLayout);
//3、往frame中添加两个按钮组件
frame.add(new Button("按钮1"));
frame.add(new Button("按钮2"));
frame.pack();
frame.setVisible(true);
}
样例输出:水平方向的按钮
样例输出:垂直方向的按钮
在Java.swing中,提供了一个新的容器Box,该容器的默认布局管理器就是BoxLayout,大多数形况下,Box容器去容纳多个GUI组件,然后再把Box容器作为一个组件,添加到其他的容器中,从而形成窗口布局。
方法名称 | 方法功能 |
static Box createHorizontalBox() | 创建一个水平排列组件的Box容器 |
static Box creatVerticalBox() | 创建一个垂直排列组件的Box容器 |
public static void main(String[] args) {
Frame frame = new Frame("测试BoxLayout");
//1、创建一个水平排列组件的Box容器
Box hBox = Box.createHorizontalBox();
//2、往当前容器中添加两个按钮
hBox.add(new Button("水平按钮1"));
hBox.add(new Button("水平按钮2"));
//3、创建一个垂直组件的Box容器
Box vBox = Box.createVerticalBox();
//4、往当前容器中添加两个按钮
vBox.add(new Button("垂直按钮1"));
vBox.add(new Button("垂直按钮2"));
//5、把两个Box容器添加到FrameZ中展示
frame.add(hBox, BorderLayout.NORTH);
frame.add(vBox);
frame.pack();
frame.setVisible(true);
}
样例输出:
但是这样的排布中间没有空隙, 那么怎么样让按钮之间有空隙呢?
public static void main(String[] args) {
Frame frame = new Frame();
//1、创建水平排列的Box容器
Box hbox = Box.createHorizontalBox();
//2、往hBox容器中添加按钮,还需要在多个按钮之间添加分割
hbox.add(new Button("水平按钮1"));
hbox.add(Box.createHorizontalGlue());//该分割在两个方向上都可以拉伸
hbox.add(new Button("水平按钮2"));
hbox.add(Box.createHorizontalStrut(30));//水平2与水平3之间不能拉伸,固定死了
hbox.add(new Button("水平按钮3"));
//3、创建垂直排列的Box容器 vBox
Box vBox = Box.createVerticalBox();
//4、往vBox容器中添加按钮,还需要在多个按钮之间添加分割
vBox.add(new Button("垂直按钮1"));
vBox.add(Box.createVerticalGlue());//该分割在两个方向上都可以拉伸
vBox.add(new Button("垂直按钮2"));
vBox.add(Box.createVerticalStrut(30));//垂直按钮2与垂直按钮3之间是定死的,无论你怎么拉伸都不变
vBox.add(new Button("垂直按钮3"));
//5、把box容器中添加到frame中
frame.add(hbox, BorderLayout.NORTH);
frame.add(vBox);
frame.pack();
frame.setVisible(true);
}
样例输出:
组件名 | 功能 |
Button | 按钮 |
Canvas | 用于绘图的画布 |
Checkbox | 复选框组件(也可以当作单选框组件使用) |
CheckboxGroup | 用于将多个Checkbox组件组合成一组,一组Checkbox组件将有只有一个可以被选中,即全部变成单选框组件 |
Choice | 下拉选择框 |
Frame | 窗口,在GUI程序里面通过该类创建窗口 |
Label | 标签类,用于放置提示性文本 |
List | 列表框组件,可以添加多个项目 |
Panel | 不能单独存在基本容器类,必须放到其他容器当中 |
Scrollbar | 滑动条组件,如果用户输入位于某个范围的值,就可以使用滑动条组件,比如调色板中设置RGB的三个值所用的滑动条。当创建一个滑动条时,必须指定它的方向、c初始值、滑块的大小、最小值以及最大值 |
ScrollPane | 带水平及垂直滚动条的容器组件 |
TextArea | 多行文本框 |
TextField | 单行文本框 |
1.如何利用上面组件组装成下面的例子
public class day12 {
//基本步骤:创建基本组件
Frame frame = new Frame();
TextArea te = new TextArea(5, 20);
Choice colorchoice = new Choice();
CheckboxGroup cbg = new CheckboxGroup();
Checkbox male = new Checkbox("男", cbg, true);
Checkbox female = new Checkbox("女", cbg, false);
Checkbox isMarried = new Checkbox("是否已婚");
TextField tf = new TextField(20);
Button ok = new Button("确认");
List colorList = new List(6, true);
public void init() {
//1、组装界面
//2、组装底部
Box bBox = Box.createHorizontalBox();
bBox.add(tf);
bBox.add(ok);
frame.add(bBox, BorderLayout.SOUTH);
//3、组装 选择部分
colorchoice.add("蓝色");
colorchoice.add("红色");
colorchoice.add("绿色");
Box cBox = Box.createHorizontalBox();
cBox.add(colorchoice);
cBox.add(male);
cBox.add(female);
cBox.add(isMarried);
//4、组装文本域与选择部分
Box topLeft = Box.createVerticalBox();
//------->组装文本区域
topLeft.add(te);
//------->组装选择区域
topLeft.add(cBox);
//5、组装顶部左边和列表框
colorList.add("蓝色");
colorList.add("红色");
colorList.add("绿色");
Box top = Box.createHorizontalBox();
top.add(topLeft);
top.add(colorList);
frame.add(top);
//设置frame为最佳大小,并且可见
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new day12().init();//调用init方法
}
}
1.首先将需要的组件列出来
2.将需要的组件组装起来
---->由上图可知先用一个box将下面的确定按钮以及文本区域组装在一起,并放在南边,由于文本区域与确定按钮是水平的,故用 :Box.createHorizontalBox()方法,组装为bBox
---->其次组装选择颜色的功能,由于也是与性别,已婚水平的也用Box.createHorizontalBox()方法,组装为cBox
---->组装最大文本域以及颜色选择,性别,已婚,由于二者是垂直关系,故用:Box.createVerticalBox()方法,将其组装成topLeft
---->最后例举colorlist,需要放在最top盒子的右边,那么再建造一个top盒子,将topLeft放在左边,colorlist放在右边,二者水平关系,用Box.createHorizontalBox()方法。
Dialog是window类的子类,是一个容器类,属于特殊组件。对话框是可以独立存在的顶级窗口,因此与普通窗口的用法几乎完全一样,但是使用对话框需要注意下面两点:
方法名称 | 方法功能 |
Dialog(Frame owner,String title,boolean modal) | 创建一个对话框对象: onwer:当前对话框的父窗口 titile:当前对话框的标题 modal:当前对话框是否为模式对话框 |
1、什么是模式对话框?
模态对话框(Modal Dialogue Box,又叫做模式对话框),是指在用户想要对对话框以外的应用程序进行操作时,必须首先对该对话框进行响应。所以无法对对话框以外的应用程序进行操作。
2、什么是非模式对话框
非模态对话框允许用户在处理非模态对话框的同时处理目标对话框。其实与上面的内容反之即可。
public static void main(String[] args) {
Frame frame=new Frame();
//1.创建两个对话框Dialog对象,一个模式,一个非模式
Dialog d1 = new Dialog(frame, "模式对话框", true);
Dialog d2=new Dialog(frame,"非模式对话框",false);
//2、通过setBounds方法设置Dialog的位置与大小
d1.setBounds(20,30,300,200);
d2.setBounds(20,30,300,200);
//3、创建两个按钮
Button b1=new Button("打开模式对话框");
Button b2=new Button("打开非模式对话框");
//4、需要给这两个按钮添加点击后的行为(AWT事件)
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
d1.setVisible(true);
}
});
b2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
d2.setVisible(true);
}
});
//5、把按钮添加到frame中
frame.add(b1,BorderLayout.NORTH);
frame.add(b2);
frame.pack();
frame.setVisible(true);
}
3、但是如何添加组件到对话框里面去呢?
其实很简单只需要将基本组件装到Box中在将Box中的对象添加到对话框即可。
public static void main(String[] args) {
Frame frame=new Frame();
//1.创建两个对话框Dialog对象,一个模式,一个非模式
Dialog d1 = new Dialog(frame, "模式对话框", true);
//----->创建一个垂直的Box容器,把一个文本框以及确定按钮放在Box容器中
Box vbox=Box.createVerticalBox();
vbox.add(new TextField(20));
vbox.add(new Button("确认"));
//----->将vbox放在d1中去
d1.add(vbox);
//2、通过setBounds方法设置Dialog的位置与大小
d1.setBounds(20,30,300,200);
//3、创建两个按钮
Button b1=new Button("打开模式对话框");
//4、需要给这两个按钮添加点击后的行为(AWT事件)
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
d1.setVisible(true);
}
});
//5、把按钮添加到frame中
frame.add(b1,BorderLayout.NORTH);
frame.pack();
frame.setVisible(true);
}
Dialog类中还有一个子类:FileDialog,它代表一个文件对话框,用于打开或者保存文件,需要注意的是FileDialog无法指定模态还是非模态,这是因为FileDialog依赖于运行平台的实现,如果运行平台的为文件对话框是模态的,那么FileDialog也是模态的;否则就是非模态的。
方法名称 | 方法功能 |
FileDialog(Frame parent,String title.int mode) | 创建一个文件对话框: parent:指定父窗口 title:对话框标题 mode:文件对话框类型,如果指定为FileDialog.LOAD,用于打开文件,如果指定为FileDialog.SAVE,用于保存文件 |
String getDirectory() | 获取被打开或保存文件的绝对路径 |
String getFile() | 获取被打开或保存文件的文件名 |
public static void main(String[] args) {
Frame frame = new Frame();
//1、创建两个FileDialog对象
FileDialog fileDialog = new FileDialog(frame, "选择要打开的文件", FileDialog.LOAD);
FileDialog fileDialog1 = new FileDialog(frame, "选择要保存的文件", FileDialog.SAVE);
//2、创建两个按钮
Button b1 = new Button("打开文件");
Button b2 = new Button("保存文件");
//3、给这两个按钮设置点击后的行为:获取打开或者保存的路径文件名
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileDialog.setVisible(true);//代码会阻塞到这里
//获取选择的路径及文件
String directory = fileDialog.getDirectory();
String file = fileDialog.getFile();
System.out.println("打开的文件路径为:" + directory);
System.out.println("打开的文件名称为:" + file);
}
});
b2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fileDialog1.setVisible(true);
//获取选择的路径及文件
String directory = fileDialog1.getDirectory();
String file = fileDialog1.getFile();
System.out.println("打开的文件路径为:" + directory);
System.out.println("打开的文件名称为:" + file);
}
});
//4、把按钮添加到Frame中
frame.add(b1, BorderLayout.NORTH);
frame.add(b2);
frame.pack();
frame.setVisible(true);
}
样例输出:
1)定义:当在某个组件上发生某些操作的时候,会自动的触发-段代码的执行。
2)在GUI事件处理机制中涉及到4个重要的概念需要理解:
事件源(Event Source) :操作发生的场所,通常指某个组件,例如按钮、窗口等;
事件( Event) : 在事件源上发生的操作可以叫做事件, GUI会把事件都封装到一个Event对象中 ,如果需要知道该事件的详细信息,就可以通过Event对象来获取。
事件监听器(Event Listener):当在某个事件源上发生了某个事件,事件监听器就可以对这个事件进行处理。
注册监听:把某个事件监听器(A)通过某个事件(B)绑定到某个事件源(C)上,当在事件源C上发生了事件B之后,那么事件监听器A的代码就会自动执行。
了解了上面机制,那么要如何实现下面图片呢,点击确定会有hello world出现?
public class day15 {
Frame frame=new Frame();
TextField tf = new TextField(30);
Button ok = new Button("确定");
public void init(){
//组装视图
//监听器
//MyListener myListener=new MyListener();
//注册监听
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tf.setText("hello world");
}
});
//把tf和ok放到Frame中
frame.add(tf,BorderLayout.NORTH);
frame.add(ok);
frame.pack();
frame.setVisible(true);
}
// private class MyListener implements ActionListener {
//
// @Override
// public void actionPerformed(ActionEvent e) {
// tf.setText("hello world");
// }
// }
public static void main(String[] args) {
new day15().init();
}
事件监听器必须实现事件监听器接口,AWT提供了大量的事件监听器接口用于实现不同类型的事件监听器,用于监听不同类型的事件。AWT中提供了丰高的事件类,用于封装不同组件上所发生的特定操作, AWT的事件类都是AWTEvent类的子类,AWTEvent是EventObject的子类。
1、AWT把事件分为了两大类:
1)低级事件:这类事件是基于某个特定动作的事件。比如进入。点击、拖放等动作的鼠标事件,再比如得到焦点和失去焦点等焦点事件。
事件 | 触发时机 |
ComponentEvent | 组件事件,当组件尺寸发生变化、位置发生移动、显示/陶藏状态发生改变时触发该事件。 |
ContainerEvent | 容器事件,当容器里发生添加组件、删除组件时触发该事件。 |
WindowEvent | 窗口事件,当窗口状态发生改变(如打开、关闭。最大化、最小化)时触发该事件。 |
FocusEvent | 焦点事件,当组件得到焦点或失去焦点时触发该事件。 |
KeyEvent | 键盘事件,当按键被按下、松开、单击时触发该事件。 |
MouseEvent | 鼠标事件,当进行单击、按下、松开、移动鼠标等动作时触发该事件。 |
PaintEvent | 组件绘制事件。该事件是一个特殊的事件类型 ,当GUI组件调用update/paint方法来呈现自身时触发该事件,该事件并非专用于事件处理模型。 |
2)高级事件:这类事件并不会基于某个特定动作,而是根据功能含义定义的事件。
事件 | 触发时机 |
ActionEvent | 动作事件,当按钮、菜单项被单击,在TextField中按Enter键时触发。 |
AjustmentEvent | 调节事件,在滑动条上移动滑块以调节数值时触发该事件。 |
ItemEvent | 选项事件,当用户选中某项,或取消选中某项时触发该事件。 |
TextEvent | 文本事件,当文本框、文本域里的文本发生改变时触发该事件。 |
2、事件监听器
不同的事件需要使用不同的监听器监听,不同的监听器需要实现不同的监听器接口,当指定事件发生后,事件监听器就会调用所包含的事件处理器(实例方法)来处理事件。
3、案例一:
public static void main(String[] args) {
Frame frame=new Frame("测试监听器");
//创建组件(事件源)
TextField tf=new TextField();
Choice names=new Choice();
names.add("刘岩");
names.add("舒淇");
names.add("闫妮");
//给文本添加TextListener,监听内容变化
tf.addTextListener(new TextListener() {
@Override
public void textValueChanged(TextEvent e) {
String text = tf.getText();
System.out.println("当前文本的内容为:"+text);
}
});
//给下拉选择框添加ItemListener,监听条目选项的变化
names.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
Object item = e.getItem();
System.out.println("当前选中的条目为:"+item);
}
});
//给frame注册ContainerListener监听器,监听容器组件的添加
frame.addContainerListener(new ContainerListener() {
@Override
public void componentAdded(ContainerEvent e) {//添加方法
Component child = e.getChild();
System.out.println("frame中添加了:"+child);
}
@Override
public void componentRemoved(ContainerEvent e) {//移除方法
}
});
//添加到frame中
Box hBox=Box.createHorizontalBox();
hBox.add(names);
hBox.add(tf);
frame.add(hBox);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
样例输出:
每次点击都会记录下监听内容:
4、 案例二:如何关闭右上角x键
public static void main(String[] args) {
Frame frame=new Frame("测试WindowListener");
frame.setBounds(200,200,500,300);
//设置WindowListener,监听用户点击右上角X的动作,则关闭窗口
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
样例输出:
前面讲了GUI界面的构建,其实就是把一些GUI的组件,按照一定的布局放在容器即可。在实际开发中,除了主界面,还有一类比较重要的内容就是菜单相关组件,可以通过菜单相关组件很方便的使用特定的功能,在AWT中,菜单组件的使用和之前学习的组件时一模一样的,只需要把菜单条、菜单、菜单项组合在一起,按照一定的布局,放入到容器中即可。
菜单组件名称 | 功能 |
MenuBar | 菜单条,菜单的容器 |
Menu | 菜单组件,菜单项的容器。它也是MenuItem的子类,所以可作为菜单项使用 |
PopupMenu | 菜单项组件 |
MenuItem | 菜单项组件 |
CheckboxMenuItem | 复选框菜单项组件 |
菜单相关组件使用:
1、准备菜单项组件,这些组件可以是MenuItem及子类对象
2、准备菜单组件Menu或者PopupMenu(右击弹出子菜单),把第一步中准备好的菜单项组件添加进来
3、准备菜单条组件MenuBar,把第二步准备好的菜单组件Menu添加进来
4、把第三步中准备好的菜单条组件添加到窗口对象中显示
小技巧:
1、如果要在某个菜单的菜单项之间添加分割线,那么只需要调用Menu的add(new MenuItem("-"))即可。
2、如果要给某个菜单项关联快捷键功能,那么只需要在创建菜单项对象设置即可,例如给菜单项关联ctrl+shift+Q快捷键,只需要:new MenuItem("菜单项名字",new MenuShortcut(KeyEvent.VK_Q,true);
案例一:如何做出下图呢?
public class day18 {
//创建窗口
private Frame frame = new Frame();
//创建菜单条
MenuBar menuBar = new MenuBar();
//创建菜单组件
Menu fileMenu = new Menu("文件");
Menu editMenu = new Menu("编辑");
Menu formatMenu = new Menu("格式");
//菜单项组件
MenuItem auto = new MenuItem("菜单换行");
MenuItem copy = new MenuItem("复制");
MenuItem paste = new MenuItem("粘贴");
//关联快捷键
MenuItem comment = new MenuItem("注释", new MenuShortcut(KeyEvent.VK_Q, true));
MenuItem canceComment = new MenuItem("取消注释");
TextArea ta = new TextArea(6,40);
public void init() {
//组装视图
comment.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//在监听器下e.getActionCommand()点击后最后都返回注释功能
ta.append("您点击菜单项:" + e.getActionCommand()+"\n");
}
});
formatMenu.add(comment);
formatMenu.add(canceComment);
//组装编辑菜单
editMenu.add(auto);
editMenu.add(copy);
editMenu.add(paste);
editMenu.add(formatMenu);
//组装菜单条
menuBar.add(fileMenu);
menuBar.add(editMenu);
//把菜单条放入到Frame中
frame.setMenuBar(menuBar);
frame.add(ta);
//设置frame最佳大小
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new day18().init();
案例二:通过PopupMenu实现下图效果
实现思路:
1、创建PopupMenu菜单组件
2、创建多个MenuItem菜单项,并添加到PopupMenu中
3、将PopupMenu添加到目标组件中
4、为需要右击出现PopupMenu菜单的组件,注册鼠标监听事件,当监听到用户释放右键时,弹出菜单。
public class day19 {
private Frame frame=new Frame("这里测试PopupMenu");
//创建文本域
TextArea ta=new TextArea("我爱中华",6,40);
//创建Panel容器
Panel p=new Panel();
//创建PopupMenu
PopupMenu popupMenu=new PopupMenu();
//创建菜单项
MenuItem comment=new MenuItem("注释");
MenuItem cancalcomment=new MenuItem("取消注释");
MenuItem copy=new MenuItem("复制");
MenuItem save=new MenuItem("保存");
public void init(){
//组装视图
ActionListener listener=new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
ta.append("您点击了右键:"+actionCommand+"\n");
}
};
//传入监听器
comment.addActionListener(listener);
cancalcomment.addActionListener(listener);
copy.addActionListener(listener);
save.addActionListener(listener);
//将功能放到popupMenu当中去
popupMenu.add(comment);
popupMenu.add(cancalcomment);
popupMenu.add(copy);
popupMenu.add(save);
//将popupMenu放到面板中Panel中去
p.add(popupMenu);
//设置Panel的大小
p.setPreferredSize(new Dimension(40,100));
//给Panel注册鼠标事件,监听用户释放鼠标的动作,展示菜单
p.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
//isPopupTrigger()用来判断是否为鼠标右键点击
boolean flag = e.isPopupTrigger();
if(flag){
//显示popupMenu
popupMenu.show(p,e.getX(),e.getY());
}
}
});
frame.add(ta);
frame.add(p,BorderLayout.SOUTH);
//设置frame最佳大小,并可视化
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new day19().init();
}
}
很多程序如各种游戏需要在窗口中绘制各种图形,除此之外,及时要开发JavaEE项目时,有时候也必须"动态"地向客户端生成各种图形、图标,比如:图形验证码、统计图等,这都需要利用AWT地绘图功能。
组件绘图原理:
之前我们已经学习很多组件,如:Button、Frame、Checkbox等等,不同的组件,展示出来的图形都不一样,其实这些组件展示出来的图形,其本质就是用AWT的绘图完成。
在AWT中,真正提供绘图功能的是Graphics对象,那么Component组件和Graphics对象存在什么关系,才能让Component绘制呢?在Component类中,提供了下列三个方法来完成组件图案的绘制于刷新;
paint(Graphics g):绘制组件的外观;
update(Graphics g):内部调用paint方法,刷新组件外观;
repaint():调用update方法,刷新组件外观;
一般情况下,update和paint方法是由AWT系统负责调用,如果程序要希望系统重新绘制组件,可以调用repaint方法完成。
1、Graphics对象的使用
实际生活中如果需要画图,首先我们得准备一张纸,然后在拿一支笔,配和一些颜色,就可以在纸上画出来各种各样的图形,例如:圆形、矩形等等。
程序绘图也一样,也需要画布,画笔,颜料等等。AWT中提供了Canvas类充当画布,提供Graphics类来充当画笔,通过调用Graphics对象的setColor()方法可以给画笔设置颜色。
2、画图的步骤
1.自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图。
2.在paint方法内部,真正开始画图之前调用Graphics对象的setColor()、setFont()等方法设置画笔的颜色,字体等属性;
3.调用Graphics画笔的drawXxx()方法开始画图。
其实画图的核心就在使用Graphics画笔在Canvas画布上画出什么颜色、什么样式的图形,什么样式的图形,所以核心在画笔上,下表中列出了Graphics类中常用的一些方法:
方法名称 | 方法功能 |
setColor(Color c) | 设置颜色 |
setFont(Font font) | 设置字体 |
drawLine() | 绘制直线 |
drawRect() | 绘制矩形 |
drawRoundRect() | 绘制圆角矩形 |
drawOval() | 绘制椭圆形 |
drawPolygon() | 绘制多边形 |
drawArc() | 绘制圆弧 |
drawPolyline | 绘制折线 |
fileRect() | 填充圆角矩形区域 |
fillRoundRect() | 填充圆角矩形区域 |
fillOval() | 填充椭圆区域 |
fillPolygon() | 填充多边形区域 |
fillArc() | 填充圆弧对应的扇形区域 |
drawImage() | 绘制位图 |
3、案例一:按钮点击成图像
public class day20 {
//定义一个RECT_SHAPE以及OVAL_SHAPE当点击按钮的时候监听器打开,将二者分别进行赋值操作
private final String RECT_SHAPE = "reat";
private final String OVAL_SHAPE = "oval";
private Frame frame = new Frame("测试绘图");
Button butreat = new Button("绘制矩形");
Button butoval = new Button("绘制椭圆");
//定义一个变量,记录当前要绘制椭圆还是矩形
private String shape = "";
//1、自定义类,继承Canvas,重写paint(Graphics g)方法完成画图;
private class Mycnvas extends Canvas {
@Override
public void paint(Graphics g) {
//绘制不同的图形
if (shape.equals(RECT_SHAPE)) {
//绘制矩形
g.setColor(Color.BLACK);//设置当前画笔的颜色
g.drawRect(100, 100, 150, 100);
} else if (shape.equals(OVAL_SHAPE)) {
//绘制椭圆
g.setColor(Color.RED);
g.drawOval(100, 100, 150, 100);
}
}
}
//创建自定义的画布对象
Mycnvas drawArea = new Mycnvas();
public void init() {
//当我点击绘制矩形的时候,就会执行监听器代码
butreat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//将定义的RECT_SHAPE赋值给shape
shape = RECT_SHAPE;
//并且在画布中repaint()
drawArea.repaint();
}
});
//当我点击绘制椭圆的时候,就会执行监听器代码
butoval.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//将定义的OVAL_SHAPE赋值给shape
shape = OVAL_SHAPE;
drawArea.repaint();
}
});
//创建Panel,承载按钮
Panel p = new Panel();
p.add(butoval);
p.add(butreat);
frame.add(p, BorderLayout.SOUTH);
//drawArea的大小需要设置
drawArea.setPreferredSize(new Dimension(300, 300));
frame.add(drawArea);
//frame自适应
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new day20().init();
}
}
4、案例二:弹射小球 (非常有意思,建议自己手动敲一遍理解原理)
Java也可以用来开发一些动画。所谓动画,就是间隔一定的时间(通常小于0.1秒)重新绘制的图形,两次绘制的图像之间差异比较小,肉眼看起来就成为所谓的动画。
为了实现间隔一定的时间就重新调用组件的repain方法,可以借助Swing提供的Timer类,Timer类是一个定时器,它有如下一个构造器:
Timer(int delay,ActionListener):每间隔delay毫秒,系统自动触发ActionLinster监听器的事件处理器的方法,在方法内部我们就可以调用组件的repaint方法,完成组件重新绘制。
package javaswing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class day21 {
private Frame frame = new Frame("弹球游戏");
//设置桌面宽度
private final int TABLE_WIDTH = 300;
//设置桌面高度
private final int TABLE_HEIGHT = 400;
//球拍的高度和宽度
private final int RACKET_WIDTH = 60;
private final int RACKET_HEIGHT = 20;
//小球的大小
private final int BALL_SIZE = 16;
//定义变量,记录小球的坐标
private int ballX = 120;
private int ballY = 20;
//定义变量,记录小球在x和y方向上分别移动的速度
private int speedY = 10;
private int speedX = 5;
//定义变量,记录球拍的坐标
private int racketX = 120;
private final int racketY = 340;//y坐标固定不变的,只会在左右移动
//定义变量,标识当前游戏是否结束
private boolean isOver = false;
//声明一个定时器
private Timer timer;
//自定义一个类,继承canvas,充当画布
private class Mycanvas extends Canvas {
//记得重写paint
@Override
public void paint(Graphics g) {
//TODO 在这里绘制内容
if (isOver) {
//游戏结束的内容
g.setColor(Color.RED);
g.setFont(new Font("Times", Font.BOLD, 30));
g.drawString("游戏结束!", 50, 200);
} else {
//游戏进行的内容
//绘制小球
g.setColor(Color.BLUE);
g.fillOval(ballX, ballY, BALL_SIZE, BALL_SIZE);
//绘制球拍
g.setColor(Color.PINK);
g.fillRect(racketX, racketY, RACKET_WIDTH, RACKET_HEIGHT);
}
}
}
//创建绘画区域
Mycanvas drawArea = new Mycanvas();
//组件组装方法
public void init() {
//组装视图,以及游戏逻辑的控制
//----->球拍的控制逻辑
//----->小球的控制逻辑
//完成球拍坐标的变化(创建监听器)(按键事件)(键盘事件)
KeyListener listener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//获取当前按下的键
int keyCode = e.getKeyCode();//我们的键盘每一个键位都对应一个整数keycode
if (keyCode == KeyEvent.VK_LEFT) {
//应该向左移动
if (racketX > 0) {
racketX -= 10;
}
}
if (keyCode == KeyEvent.VK_RIGHT) {
//应该向右移动
if (racketX < (TABLE_WIDTH - RACKET_WIDTH)) {
racketX += 10;
}
}
}
};
//给frame和drawArea注册监听器
frame.addKeyListener(listener);
drawArea.addKeyListener(listener);
//小球坐标的控制
ActionListener task = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//更新小球的坐标,重绘界面
//根据边界范围,修正速度
//----->修正x轴弹出碰到边界的方向
if (ballX <= 0 || ballX >= (TABLE_WIDTH - BALL_SIZE)) {
speedX = -speedX;
}
//----->修正y轴弹出碰到边界的方向
if (ballY <= 0 || ballY > racketY - BALL_SIZE && ballX > racketX && ballX < racketX + RACKET_WIDTH) {
speedY = -speedY;
}
//----->超出球拍往下,游戏结束
if (ballY > racketY - BALL_SIZE && (ballX < racketX || ballX > racketX + RACKET_WIDTH)) {
//当前小球超出了球拍范围
//停止定时器
timer.stop();
//修改游戏是否结束标记
isOver = true;
//重新绘制界面
drawArea.repaint();
}
ballX += speedX;
ballY += speedY;
//重新绘制界面
drawArea.repaint();
}
};
//这里需要手动导包import javax.swing.Timer.*;
timer = new Timer(100, task);
timer.start();
drawArea.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));
frame.add(drawArea);
//frame窗口关闭
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//frame最佳大小
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new day21().init();
}
如果仅仅绘制一些简单的几何图形,程序的图形依旧十分简单。AWT允许在组件上绘制位图,Graphics提供了drawmage(Image image)方法用于绘制位图,该方法需要一个Image参数一一代表位图,通过该方法就可以绘制出指定的位图。
位图使用步骤:
1、创建Image的子类对象BufferedImage(int width,int heigth,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;
2、调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一样。
3、调用组件paint()方法中提供的Graphics对象的drawImage()方法,一次性的内存中的图片BufferedImage绘制到待定的组件上。
使用位图绘制组件的好处:
使用位图来绘制组件,相当于实现了图的缓冲区,此时绘图时没有直接把图形绘制到组件上,而是先绘制到内存中的BufferedImage上,等全部绘制完成后,再一次性的图像显示到组件上即可,这样用户的体验会好很多。
案例:
绘画不精,望谅解,重点是:
drawArea.addMouseMotionListener与drawArea.addMouseListener的区别,我就是在这里踩坑的。
drawArea.addMouseMotionListener指的是按下鼠标移动的
drawArea.addMouseListener值的是不移动鼠标的
public class day22 {
private Frame frame = new Frame("简单绘图工具");
//定义画图区域的宽高
private final int AREA_WIDTH = 500;
private final int AREA_HEIGHT = 400;
//定义一个右键菜单,用于设置画笔的颜色,PopupMenu为鼠标右击
private PopupMenu colorMenu = new PopupMenu();
private MenuItem reditem = new MenuItem("红色");
private MenuItem buleitem = new MenuItem("蓝色");
private MenuItem greenitem = new MenuItem("绿色");
//定义一个变量记录当前画笔的颜色
private Color forceColor = Color.BLACK;
//创建一个BufferImage位图对象
BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_RGB);
//通过位图,获取关联的Graphics对象
Graphics g = image.getGraphics();
//自定义一个类,继承Canvas
private class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
//取自定义类中对象drawArea绘图区域
MyCanvas drawArea = new MyCanvas();
//定义一个变量,记录鼠标在拖动过程中上一次所处的坐标
private int preX = -1;
private int preY = -1;
//组装方法
public void init() {
//组装视图,逻辑控制
ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//e.getActionCommand()获取的是new MenuItem("红色")中的“红色”
String actionCommand = e.getActionCommand();
switch (actionCommand) {
case "红色":
forceColor = Color.RED;
break;
case "蓝色":
forceColor = Color.BLUE;
break;
case "绿色":
forceColor = Color.GREEN;
break;
}
}
};
//将按钮添加监听器并使之进行监听
reditem.addActionListener(actionListener);
buleitem.addActionListener(actionListener);
greenitem.addActionListener(actionListener);
//colorMenu
colorMenu.add(reditem);
colorMenu.add(buleitem);
colorMenu.add(greenitem);
//把colorMenu放在绘图区域
drawArea.add(colorMenu);
drawArea.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {//当鼠标抬起时被调用
boolean popupTrigger = e.isPopupTrigger();
if (popupTrigger) {
//将colorMenu在drawArea展现出来,鼠标点击的x与y轴位置
colorMenu.show(drawArea, e.getX(), e.getY());
}
}
});
//设置位图背景为白色
g.setColor(Color.white);
g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);
//通过监听鼠标的移动,完成线条绘制
drawArea.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
if (preX != -1 && preY != -1) {
g.setColor(forceColor);
//画线条,需要两组坐标,分别代表起点与终点,e.getX(),e.getY()可以获取坐标
g.drawLine(preX, preY, e.getX(), e.getY());
}
//修正preX与preY的值
preX = e.getX();
preY = e.getY();
//重绘组件
drawArea.repaint();
}
});
drawArea.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
//重置preX和preY
preX = -1;
preY = -1;
}
});
//将绘画区域设置为最合适的区域
drawArea.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
frame.add(drawArea);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new day22().init();
}
}
在实际生活中,很多软件都支持打开本地磁盘已经存在的图片进行编辑,编辑完毕后,再重新保存到本地磁盘。如果使用AWT要完成这样的功能,那么需要使用到ImageIO类,可以操作本地磁盘的图片文件。
方法名称 | 方法功能 |
static BufferedImage read(File input) | 读取本地磁盘图片文件 |
static BufferedImage read(InputStream input) | 读取本地磁盘图片文件 |
static boolean write(RenderedImage im,String formatName,File output) | 往本地磁盘中输出图片文件 |
案例一:制作图片查看器
package javaswing;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class day23 {
private Frame frame = new Frame("图片查看器");
//创建菜单条
MenuBar menuBar = new MenuBar();
//创建菜单
Menu menu = new Menu("文件");
//创建菜单中的菜单名
MenuItem open = new MenuItem("打开");
MenuItem save = new MenuItem("另存为");
//声明BufferImage对象,记录本地存取到内存的图片,到最后读入文件,直接赋值给image
BufferedImage image;
private class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
MyCanvas drawArea = new MyCanvas();
public void init() {
//组装视图
open.addActionListener(e -> {
//打开一个文件对话框
FileDialog fileDialog = new FileDialog(frame, "打开图片", FileDialog.LOAD);
fileDialog.setVisible(true);
//获取用户选择的图片路径以及名称
String dir = fileDialog.getDirectory();
String fileName = fileDialog.getFile();
try {
//记得这里要写IO读入的文件,并且赋值给image
image = ImageIO.read(new File(dir, fileName));
drawArea.repaint();
} catch (IOException ex) {
ex.printStackTrace();
}
});
save.addActionListener(e -> {
//获取用户设置的保存路径以及文件名称
FileDialog fileDialog = new FileDialog(frame, "保存图片", FileDialog.SAVE);
fileDialog.setVisible(true);
//展示用户设置的保存路径以及文件名称
String dir = fileDialog.getDirectory();
String fileName = fileDialog.getFile();
try {
//注意这里要写写”JPEG“,文件格式名
ImageIO.write(image, "JPEG", new File(dir, fileName));
} catch (IOException ex) {
ex.printStackTrace();
}
});
//将打开文件以及保存文件放到menu组件中
menu.add(open);
menu.add(save);
//将menu组件放在菜单栏中
menuBar.add(menu);
//把菜单条放在窗口中
frame.setMenuBar(menuBar);
frame.add(drawArea);
//自定义frame的框架,并且可见
frame.setBounds(200, 200, 740, 508);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) {
new day23().init();
}
}