`
datoplay
  • 浏览: 1614137 次
文章分类
社区版块
存档分类
最新评论

Java 语言程序设计【13~15章】(Word版)

 
阅读更多

第十三章 使用Swing组件一
本章要点
l 框架
– 框架是一个图形界面程序的主窗口
– 在Java中,每个具有图形界面的程序至少要有一个框架,小程序有时也会使用框架
– 框架是由边框、标题栏、最大化、最小化、还原、移动、关闭按钮、系统菜单,以及内容窗格组成
– 内容窗格是框架的核心区域,主要的图形界面组件、菜单栏、工具栏都在内容窗格中
– 框架是一个程序的主窗口,每个具有图形界面的程序都至少要有一个框架
– 如果程序中还有其它窗口,并且这些窗口依赖于框架的,则应使用对话框(JDialog)
– 如果要使其它窗口显示在框架的内部,则应该使用内部框架(JInternalFrame)
– 内部框架虽然和框架很象,但它不是顶级容器组件,而是专用的容器组件
– 框架是JFrame类的对象。创建并显示一个框架的步骤如下:
– 创建一个JFrame对象,此时框架尚未显示
– 在添加了组件之后,调用pack函数排列组件
– 调用它的setVisible函数以显示框架
FrameDemoFrame frame = new FrameDemoFrame();
frame.setTitle("FrameDemo - 框架功能演示");
frame.pack();
frame.setVisible(true);
– 标题用来描述程序,它显示在框架的标题栏上,以及系统的任务栏上
– 有两种设定框架标题的方法
l 利用setTitle函数设定标题
l 在构造函数中设定标题
– 调用框架的setTitle函数,如下所示:
frame.setTitle("FrameDemo - 框架功能演示");
– 利用setIconImage函数为程序设置图标
String imgURL = "myicon.gif";
ImageIcon icon = new ImageIcon(imgURL);
frame.setIconImage(icon.getImage());
– 关闭程序的方法1:将关闭窗口操作设为退出程序:
– frame.setTitle("FrameDemo - 框架功能演示");
– frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
– frame.pack();
– 参数代表关闭时的操作,可以有如下值:
l WindowConstants.DO_NOTHING_ON_CLOSE:不做任何事,用窗口监听器进行关闭工作
l WindowConstants.HIDE_ON_CLOSE:隐藏界面,这是框架和对话框对象的默认操作
l WindowConstants.DISPOSE_ON_CLOSE:隐藏窗口,并且释放它使用的资源,这是内部框架对象的默认操作
l JFrame.EXIT_ON_CLOSE:退出应用程序,这种退出方式只能用于应用程序的主框架中
– 方法2:编写窗口监听器类,在窗口关闭事件中关闭程序,如下面的代码所示:
frame.setDefaultCloseOperation(
WindowConstants.DO_NOTHING_ON_CLOSE);
frame. addWindowListener(new
FrameDemoFrame_WindowListener_Closing(frame));
– 例题:在退出程序时,要求用户确认:
public void windowClosing(WindowEvent e) {
int returnValue;
returnValue = JOptionPane.showConfirmDialog(null, "确实要退出程序吗?", "退出程序", JOptionPane.YES_NO_OPTION);
if (returnValue == JOptionPane.YES_OPTION)
System.exit(0);
}
– 调用此函数时会自动弹出一个对话框,要求用户确定是否退出程序
– 在框架中加入组件的方法:
– 用getContentPane函数获得内容窗格
– 在内容窗格中用add函数加入各种组件
JPanel contentPane;
contentPane = frame.getContentPane();
JPanel panel1 = new JPanel();
contentPane.add(panel1);
panel1.add(new JLabel("请点击下面的按钮:"));
panel1.add(new JButton("关闭"));
– 主程序框架一般还有菜单栏。主程序中的菜单栏通过setJMenuBar函数来设定:
– JMenuBar menuBar = new JMenuBar();
– frame.setJMenuBar(menuBar);
– JMenuBar是菜单对象,实际中还需要在其中加入菜单项和菜单子项
– 关于菜单,后面还会有详细介绍
– 可以通过setBounds函数来实现setSize, setLocation这两个函数的功能:
public void setBounds(int x, int y, int width, int height);
Rectangle r = new Rectangle(int x, int y, int width, int height);
public void setBounds(Rectangle r);
– setLocationRelativeTo函数,它的作用是将框架放到屏幕上一个现有的组件旁边:
public void setLocationRelativeTo(Component c);
– 其中c是一个已经在屏幕上显示的组件
– 此函数将框架放到组件c的旁边,放置原则是让放置的框架尽量靠近屏幕中间
– 如果函数参数为null,那么就将框架居中放置,这是常用的一种将窗口居中放置的方法
– 对话框是另一类常用的顶级容器
– 对话框与框架非常相似,复杂的对话框也能够实现框架的几乎所有功能
– 实际中,常用的是界面简单、功能高度特化的对话框
l 消息框:显示一条消息
l 选择框:让用户进行选择
l 文件选择框:选择文件进行打开、保存等操作
l 颜色选择框:从调色板中选择颜色
– 每个对话框都必须依附于框架
l 框架关闭以后,所有依附它的对话框也会关闭
l 当框架最小化成一个图标的时候,所有依附它的对话框也会自动隐藏
l 当框架还原的时候,对话框又会自动显示
l 这些操作由Swing自动实现
– 本小节主要介绍通用的对话框(JDialog)和消息框(JOptionPane)
– 例:在主框架程序中添加一个通用对话框,里面有一个文字标签和一个按钮:
public void showDialog2() {
JDialog dialog = new JDialog(this, "对话框");
JPanel panel1 = new JPanel();
dialog.getContentPane().add(panel1);
panel1.add(new JLabel("这是一个对话框。"));
panel1.add(new JButton("点击这里"));
dialog.pack();
dialog.setVisible(true);
}
– 对话框分为有模式的和无模式的两种类型
– 有模式的对话框在显示时,将屏蔽用户向对话框所属的主框架键入的所有内容,用户不能对主框架进行任何操作
– 无模式对话框在显示时,用户仍然可以用鼠标点击主框架,在主框架里输入内容,或移动主框架
– 缺省的JDialog对话框都是无模式对话框
– 如果要创建有模式对话框,可以在构造函数里增加一项如下:
JDialog dialog = new JDialog(this, "对话框", true);
– 也可以用setModal函数来设置对话框是有模式的还是无模式的:
dialog.setModal(true); // 将对话框设置成有模式的
dialog.setModal(false); // 将对话框设置成无模式的
– 消息框(JOptionPane)是一种非常简洁、专用的对话框。
– 例:弹出一个提示消息框:
JOptionPane.showMessageDialog(frame, "修改已经保存到文件中。");
– 消息框中的图标:
l 预设的图标有4种:错误图标、消息图标、警告图标和提问图标
l 可以显示自己定制的图标
– 不同界面风格下的图标风格:
l Windows风格
l Java风格
l CDE/Motif风格
l GTK+风格
l 对话框
– 常用消息框的调用:
– 消息框:利用静态函数showMessageDialog显示
– 确认框:利用静态函数showConfirmDialog显示
– 选择框:利用静态函数showOptionDialog显示
– 输入框:利用静态函数showInputDialog显示
l 对话框
– showMessageDialog(显示消息框):
– 消息框是最简单的一种对话框,它的作用只是显示一条消息,它只有一个“确认”按钮,用以关闭消息框。
– 通过修改showMessageDialog函数的参数,可以修改消息框显示的消息、图标和消息框的标题,下面列出常用的消息框形式
– showMessageDialog是一个静态成员函数,可以从类直接调用,它有三种形式:
static void JOptionPane.showMessageDialog(Component
parentComponent, Object message)
static void JOptionPane.showMessageDialog(Component
parentComponent, Object message, String title,
int messageType)
static void JOptionPane.showMessageDialog(Component
parentComponent, Object message, String title,
int messageType, Icon icon)
– int messageType:消息框类型,有下列值:
– showConfirmDialog(显示确认框):
– 显示一个对话框,让用户选择“是”或“否”,用户选择的结果在返回值中给出:
int choice = JOptionPane.showConfirmDialog(frame, "你是否真的要删除文件?");
– 执行程序,在屏幕上显示的确认框如下:
– 点击“是”,“否”,“撤销”都会关闭确认框,同时返回一个值:
l “是”对应的返回值是JOptionPane.YES_OPTION
l “否”对应的返回值是JOptionPane.NO_OPTION
l “撤销”的返回值JOptionPane.CANCEL_OPTION
– 可以通过用条件语句比较返回值来确认用户点击的是哪个按钮,再进行下一步操作。
– showConfirmDialog是一个静态成员函数,可直接通过类来调用
– showOptionDialog(显示选择框):
– 用于显示定制的对话框,它是参数最多、使用最灵活的JOptionPane类对话框
– 可以在对话框上显示多个按钮
– 每个按钮的文字可以自己定制
– 对话框中还包括图标、提示字符串
– 甚至可以在对话框中加入组件
– 例:一个定制的对话框:
String[] options = {"小说", "诗歌", "不告诉你"};
int choice = JOptionPane.showOptionDialog(frame, "如果将你放逐到荒岛,你最希望带本什么书?", "问卷调查",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
new ImageIcon("about.png"), options, options[2]);
– showInputDialog(显示输入框):
– 该函数弹出一个对话框,提示用户输入一行文字,或者从下拉框中选择一项
– 输入框有很多用法,常用的有:
l 输入一行文字
l 带有缺省值的文本输入
l 从下拉框中选择一项内容
– 例,用于输入文字的对话框:
String rtnStr = JOptionPane.
showInputDialog(frame,
"如果将你放逐到荒岛,
你最希望带本什么书?");
– 输入文字,然后回车或单击“确认”,输入的文字会保存在rtnStr所指向的字符串里
– 如果按了“Esc”键,或单击“撤销”,那么键入的文字不会保存,rtnStr的值为空
– 可以在输入框中给出输入文字的默认值:
String rtnStr = JOptionPane.showInputDialog(frame,
"如果将你放逐到荒岛,你最希望带本什么书?",
"小说");
– 这条语句在屏幕上显示的对话框如下:
– 也可以定制对话框的标题和图标类型:
String rtnStr = JOptionPane.showInputDialog(frame,
"如果将你放逐到荒岛,你最希望带本什么书?",
"问卷调查", JOptionPane. WARNING_MESSAGE);
– 在屏幕上显示的对话框如下,注意此时不能设定输入的默认字符串。
– 可以用下拉框的形式选择内容:
String[] inputOptions = {"小说", "诗歌", "不告诉你"};
Object rtnStr = JOptionPane.showInputDialog(this, "如果将你放逐到荒岛,你最希望带本什么书?", "问卷调查", JOptionPane. QUESTION_MESSAGE, null, inputOptions, "诗歌");
– 例:带缺省值的定制文本框:
Object rtnStr = JOptionPane.showInputDialog(this,
"如果将你放逐到荒岛,你最希望带本什么书?",
"问卷调查",
JOptionPane.QUESTION_MESSAGE, null,
null, "诗歌");
l 面板
– 面板是最常用的容器组件。缺省情况下,面板上除了背景色以外没有任何东西
– 可以用多个面板,配合不同的布局管理器来配置控件在界面上的布局
l 滚动条面板
– 滚动条面板可以在屏幕上有限的区域内显示更大范围的内容,
– 相应的代码如下:
JScrollPane scrollPane = new JScrollPane(textArea1,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setPreferredSize(new Dimension(250, 250));
– 创建滚动条面板的过程分两步:
l 第一步,创建一个JScrollPane对象,在构造函数中需要给出放在面板里的控件
l 第二步,设置面板的初始大小
l 分隔条面板
– 分隔条面板将一个面板分为两部分,两部分之间被一个分隔条隔开,用户可以拖动分隔条来改变左右面板的大小
– 分隔条面板由JSplitPane对象实现
– 左右两部分分别是两个子面板,子面板一般是滚动条面板
– 下面的代码用来创建分隔条面板:
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrolll, scroll2);
split.setDividerSize(8);
split.setOneTouchExpandable(true);
getContentPane().add(split, BorderLayout.CENTER);
l 标签页面板
– 创建标签页面板的程序代码如下:
JTabbedPane tabbed = new JTabbedPane();
tabbed.addTab("第一页", null, scrolll, "这是第一页内容");
tabbed.addTab("第二页", null, scroll2, "这是第二页内容");
– 第一步:创建一个JTabbedPane对象,使用缺省的构造函数即可
– 第二步:调用addTab函数将子面板一个个加到标签页中
l 工具栏
– 工具栏是图形界面程序常用的组件,它将一组按钮排成一行,放在程序的顶端
– 工具栏一般作为菜单栏的补充,为菜单栏中常用的命令提供一种快捷的执行方法
– 工具栏通过JToolBar对象来实现:
JToolBar toolBar = new JToolBar();
toolBar.add(button1);
toolBar.add(button2);
frame.getContentPane().add(toolBar, BorderLayout.PAGE_START);
– 工具栏中还可以设置分隔条,分隔条将按钮分隔开,以达到分组的目的:
– 分隔条通过addSeparator函数来添加
toolBar.add(button2);
toolBar.addSeparator();
toolBar.add(button3);
l 内部框架
内部框架常用于多文档界面,它的特点是在主框架的内部还有一系列子框架:

– 创建内部框架的基本步骤如下:
– (1) 创建内部框架对象
JInternalFrame subFrame1 = new JInternalFrame(
"文档1", true, true, true, true);
– 内部框架对象的构造函数有5个参数:
l 参数1:显示在子框架标题栏上的名称;
l 参数2:设定子框架能否改变大小;
l 参数3:设定子框架能否关闭;
l 参数4:设定子框架能否最大化;
l 参数5:设定子框架能否缩成一个图标。需要注意的是,子框架缩成的图标是在主框架的左下角,而不会在操作系统的任务栏出现:
– (2) 设置子框架对象的属性
– 子框架的属性包括子框架大小、位置,显示子框架,将子框架设置成选中状态,如下面的代码所示:
subFrame1.setSize(300, 300);
subFrame1.setLocation(30, 30);
subFrame1.setVisible(true);
try { subFrame1.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
– (3) 将子框架放在桌面面板上
– 子框架不能放在普通的面板上,必须放到桌面面板(JDesktopPane)上
– 将桌面面板作为主框架的内容窗格,再将子框架放到桌面面板上,如下面的代码所示:
JDesktopPane desktop = new JDesktopPane();
desktop.setBackground(new Color(160, 160, 160));
desktop.add(subFrame1);
setContentPane(desktop);
– 通过这三步以后,内部框架界面就建立了
– 内部框架不是真正的顶级容器框架
– 但它也有自己的内容窗格,可以在其中放置各种面板和控件
– 内部框架常用于多文档界面,例如同时打开多个文本文件或多个图片的情况
l 分层窗格
– 分层窗格是一个可以有多层的容器,作用是让相互重叠的组件可以按照它们重叠的次序显示,上层的组件覆盖在下层组件之上
l 根窗格
– 根窗格直属于顶级的框架或对话框,包含了内容窗格和菜单栏,以及分层窗格、玻璃窗格等不可见的功能窗格,它的作用是管理所有这些窗格

第十三章 使用Swing组件二
本章要点
基本控件
l 基本控件
– 基本控件包括:
l 按钮,单选框、复选框、下拉框、列表框、菜单、文本框
– 它们是组成图形用户界面的原子控件,它们的作用主要是从用户那里得到输入,同时也显示一些简单的状态。
l 按钮
– Swing中的按钮有三种类型:
l 普通按钮(JButton)、单选框(JRadioButton)、复选框(JCheckBox)
– 这三类按钮都从AbstractButton(抽象按钮)类继承而来
– 它们的外形、功能各不相同,下面分别介绍这三类按钮
l 普通按钮
– 与按钮有关的代码如下:
panel = new JPanel();
b1 = new JButton("向后", new ImageIcon("prev.gif"));
b2 = new JButton("显示当前");
b3 = new JButton("向前", new ImageIcon("next.gif"));
panel.add(b1);
panel.add(b2);
panel.add(b3);
– 第1、3个按钮是文字与图片混合显示按钮,第2个按钮是文字按钮
– 在调用缺省构造函数创建按钮之后,可以调用setText和setIcon函数为按钮设置显示文字和图标,函数形式如下:
JButton.setText(String text);
JButton.setIcon(Icon defaultIcon);
– 按钮有两个常用的属性:
l 可用性(enable)、缺省按钮(default button)
– 按钮的可用性通过setEnabled函数来设置,这个函数只有一个参数:
l 如果参数值为真,则按钮可用
l 如果参数值为假,则按钮变成灰色,不可用
– isEnabled函数判断一个函数是否可用
– 例题:将按钮1设成可用:
if (!button1.isEnabled())
button1.setEnabled(true);
– 用setDefaultButton函数来设置缺省按钮,用getDefaultButton函数获得缺省按钮
– 下面的例子演示了如何设置缺省按钮:
frame.getRootPane().setDefaultButton(button1);
– 缺省按钮在按钮的边缘会有一条间隙:
基本控件
l 单选框
– 单选框(JRadioButton)在图形界面上显示为若干小圆点
– 单选框的作用是在几个选项中选一项,且只能选一项。
– 产生单选框的步骤:
– (1) 建立单选框对象
JRadioButton radio1 = new JRadioButton("鱼");
JRadioButton radio2 = new JRadioButton("熊掌", true);
– 只有分为一组的单选框才有单选的关系
– 将单选框分组的代码如下:
ButtonGroup group = new ButtonGroup();
group.add(radio1);
group.add(radio2);
– (3) 将单选框放到面板中
panel.add(radio1);
panel.add(radio2);
– 对于这种情况,就不需要给单选框添加监听器了,如下面的代码:
if (radio1.isSelected())
System.out.println("你选择了鱼。");
if (radio2.isSelected())
System.out.println("你选择了熊掌。");
– 在“确定”按钮的事件处理函数中,调用单选框的isSelected函数查询它是否被选中
l 复选框
– 复选框(JCheckBox)与单选框的作用相似,也是选择一些选项,不同的是这些选项之间没有相互排斥的关系
– 复选框的外形是方形小框 ,如果你选中了一个复选框,小框中会出现一个对勾 。
– 创建复选框的代码如下:
JCheckBox check1 = new JCheckBox("音乐");
JCheckBox check2 = new JCheckBox("文学");
JCheckBox check3 = new JCheckBox("体育", true);
JCheckBox check4 = new JCheckBox("电视");
JCheckBox check5 = new JCheckBox("旅游", true);
– 复选框的创建分两步:
l 创建一个复选框控件对象
l 将复选框控件放到面板上
– 在函数中,调用复选框的isSelected函数查询它是否被选中,如下面的代码所示:
System.out.print("你的兴趣爱好有:");
if (check1.isSelected())
System.out.print("音乐 ");
if (check2.isSelected())
System.out.print("文学 ");
if (check3.isSelected())
System.out.print("体育 ");
if (check4.isSelected())
System.out.print("电视 ");
if (check5.isSelected())
System.out.print("旅游 ");
l 下拉框
– 下拉框又叫组合框(JComboBox),是一种多个选项中选择一个的组件
– 从功能上看,它和前面的单选框很像,但是下拉框平时只占用一行文本的空间
– 只有当用户点击时才会向下弹出所有的选项,因此它比单选框要节省空间
– 下拉框可以很方便地增加、删除选项
– 下拉框分为两类:
l 不可编辑的下拉框:用户只能从预设的项中选择
l 可编辑的下拉框:允许用户编辑、修改文本
– 下拉框由两部分组成:
l 文本框,它一直存在,显示用户选择的文本;
l 弹出式列表,显示所有预设的选项,它只有在用户点击下拉框右侧的三角形按钮时才会出现
String[] comboStr1 = {"10岁以下", "11~20岁", "20~30岁",
"30~40岁", "40~50岁", "50~60岁", "60岁以上"};
JComboBox combo1 = new JComboBox(comboStr1);
panel.add(combo1);
– 创建下拉框的步骤是:
l 建立一个字符串数组,将所有预设的选项都存储在字符串数组中
l 创建下拉框对象,将字符串数组放到下拉框中
l 将下拉框对象放到面板上
– 创建可编辑的下拉框,需要调用setEditable函数设置下拉框的属性:
String[] comboStr2 = {"足球","篮球","羽毛球","乒乓球","象棋","围棋"};
JComboBox combo2 = new JComboBox(comboStr2);
combo2.setEditable(true);
panel.add(combo2);
– 当下拉框收起时,它变成一个文本框,你可以在其中输入文字
– 输入的内容只在当前有效,一般来说并不能成为预设的选项
– 与单选框、复选框一样,下拉框一般也是在用户点击“确定”按钮之后才处理其内容
– getSelectedItem函数,得到选择的文本
– getSelectedIndex函数,得到选择的项
– 下面这段代码演示了这两个函数的功能:
System.out.println("你选择的体育运动是:" + combo2.getSelectedItem());
System.out.println("它是下拉框中的第" + combo2.getSelectedIndex() + "项");
– 对于不可编辑的下拉框,函数返回的是用户选择的内容;对于可编辑的下拉框,函数返回的是经用户编辑以后的文本;
– getSelectedIndex函数的作用是返回用户所选的选项的序号。序号是从0开始计数的
– 如果用户没有选任何项,则返回“-1”;
– 对于可编辑下拉框,如果用户手动修改了下拉框的内容,使它与任何一项预设的选项都不相同,则函数也返回“-1”;
– 还有两个函数也很常用:
l getItemAt函数,获得某项选项的内容,
l getItemCount函数,获得选项的数量。
– 这两个函数相结合,可以得到所有的预设选项,如下面的代码所示:
for (int i = 0; i < combo2.getItemCount(); i++)
System.out.println(combo2.getItemAt(i));
– 下拉框还提供一系列函数用于添加、插入、删除选项,这些函数列举如下:
// 在预设选项列表的最后添加一项,anItem为添加的字符串对象
void addItem(Object anItem);
// 在预设列表中插入一项,anItem为添加的字符串对象,index为插入的位置
void insertItemAt(Object anItem, int index);
// 移去内容与anItem相同的预设选项
void removeItem(Object anItem);
// 移去序号为index的预设选项
void removeItemAt(int index);
// 移去所有的预设选项
void removeAllItems();
l 列表框
– 列表框的建立分以下三步:
– (1) 创建列表框对象
– 列表框是JList类的对象。与下拉框类似,在创建列表框对象时,一般需要在构造函数中通过一个字符串数组给出选项内容:
String[] listStr = {"足球","篮球","排球","网球","羽毛球",
"乒乓球","游泳","长跑","登山","象棋","围棋","桥牌"};
JList list = new JList(listStr);
– (2) 设置列表框属性
– 在创建列表框以后,需要设置它的三个属性:选项模式、布局方向以及可见的行数
– 选项模式有三种:
l 单项选择:只能选择一项
l 区间选择:可以选择一个区间范围
l 多项选择:可以选择一个区间,或几个文件
– 选项模式通过setSelectionMode函数来设定,函数的参数表示三种选择模式:
// 设置为单项选择模式
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// 设置为区间选择模式
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
// 设置为多项选择模式
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
– 列表框的布局方向也有三种:
l 横向换行:选项按行排列,一行放不下时换行放置,如果行数太多,就添加上下滚动的竖滚条;
l 纵向换列:选项按列排列,一列放不下的话就换一列放置,如果列数太多,就添加左右滚动的横滚条;
l 纵向布局:简单地将选项放在一列中,如果项目数太多,就添加竖滚条以上下滚动;
l 缺省的布局方向是纵向布局
– 布局方向通过setLayoutOrientation函数来设置,该函数的参数表示三种布局方向,如下面的代码所示:
// 设置为横向换行
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
// 设置为纵向换列
list.setLayoutOrientation(JList.VERTICAL_WRAP);
// 设置为纵向布局
list.setLayoutOrientation(JList.VERTICAL);
– 可见的行数表示列表框在没有滚动条的情况下所显示的行数,-1表示显示尽可能多的行,如下面的代码所示:
list.setVisibleRowCount(-1);
– (3) 将列表框放到一个滚动条面板中
– 一般来说,列表框中包括的项目数都多于它能显示的数量,因此需要将列表框放到一个滚动条面板中,以便在需要时滚动
– 如下面的代码所示:
JScrollPane scrollpanel = new JScrollPane(list);
scrollpanel.setPreferredSize(new Dimension(250,80));
– 常用的处理列表框的函数:
l getSelectedIndex,返回第一个选中的选项的序号
l getSelectedValue,返回第一个选中的选项的值;
l getSelectedIndices,返回一个整数数组,其中保存有所有选中的选项的序号;
l getSelectedValues,返回一个对象数组,其中保存有所有选中的选项的内容;
l isSelectedIndex,函数的作用是判断某一项选项是否被选中。
– 例题,列表框功能演示:
System.out.println("你选择的第一项是:" + list.getSelectedValue() + ",它的序号是:" + list.getSelectedIndex());
System.out.println("你选择的所有项的内容和序号列举如下:");
int[] allIndices = list.getSelectedIndices();
Object[] allValues = list.getSelectedValues();
for (int i=0; i<allIndices.length; i++)
System.out.println(" " + allValues[i] + " " + allIndices[i]);
System.out.println("检验第二项是否被选中:" + list.isSelectedIndex(1));
l 菜单
– 菜单几乎是每个应用程序的必备项,通过菜单将程序的功能组织在一起
– 菜单有两种表现形式:菜单栏和弹出式菜单
l 菜单栏一般位于主框架的顶端,包含了程序的全部功能
l 弹出式菜单只有在用户单击鼠标右键时才会出现,它给出了鼠标所指向组件的一些常用功能
– 在Swing中,菜单栏通过JMenuBar实现,弹出式菜单通过JPopupMenu实现。
– 建立菜单栏需要的三个步骤如下:
– (1) 建立菜单栏对象
JMenuBar menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);
– 首先,建立一个JMenuBar对象,
– 随后,利用框架的setJMenuBar函数将菜单栏对象放到框架的顶端
– (2) 建立菜单组
– 菜单组的含义是一组菜单项,通过点击菜单组可以显示它所包含的菜单项,如下图中的“文件”,“帮助”就是两个菜单组。
– 菜单组是JMenu对象,下面的代码建立了一个菜单组并把它加入到菜单栏中:
JMenu menu;
menu = new JMenu("文件");
menuBar.add(menu);
– (3) 建立菜单项
– 菜单项具有特定的功能,菜单项一般都会加入动作监听器,以响应用户点击菜单项的动作,完成特定的功能
– 下面的代码将一个菜单项加入菜单组中,并为它添加动作监听器:
JMenuItem menuItem;
menuItem = new JMenuItem("打开文件");
menuItem.addActionListener(new MenuActionListenerClass(this));
menuItem.setActionCommand("打开文件");
menu1.add(menuItem);
– 弹出式菜单通过JPopupMenu对象实现,建立弹出式菜单的过程与菜单栏大同小异:
JPopupMenu popup;
popup = new JPopupMenu();
JMenuItem menuItem;
menuItem = new JMenuItem("打开文件");
menuItem.addActionListener(new MenuActionListenerClass(this));
menuItem.setActionCommand("打开文件");
popup.add(menuItem);
– 为了能够在鼠标右键单击时弹出菜单,需要建立一个鼠标监听器类,响应鼠标消息:
class PopupListener extends MouseAdapter {
MenuBarDemo frame;
public PopupListener(MenuBarDemo frame) {
this.frame = frame; }
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
frame.popup.show(e.getComponent(), e.getX(), e.getY()); }
}
– (2) 为组件添加鼠标监听器
– 下面的代码为文本区域组件添加鼠标监听器,当鼠标在文本区域内单击右键时,即可弹出相应的菜单:
textArea1.addMouseListener(new PopupListener(this));
– 编译并运行程序,在文本区域里点击鼠标右键,可以看到弹出式菜单,
l 文本框
– 文本框是一种非常简单、也非常常用的文本控件,它可以让用户输入一行简短的文字。
l 不可编辑组件
– 在用户图形界面中,有些组件的作用仅仅是向用户提示,而不需要用户输入内容,这类组件称为不能编辑的组件
– 不可编辑的组件主要包括标签、进度条和工具提示
l 标签
– 标签是最常用的组件,它的作用是在界面上显示一行文字
– 下面的代码创建并显示一个标签:
JLabel label1 = new JLabel("文字标签");
panel.add(label1);
– 标签也可以显示图片,或者同时显示图片和文字。图片是通过ImageIcon对象建立的,如下面的代码所示:
ImageIcon icon1 = new ImageIcon("icon1.gif");
JLabel label1 = new JLabel(icon1);
JLabel label2 = new JLabel("图形标签", icon1, JLabel.CENTER);
– 需要注意的是,如果在标签的构造函数中同时给出文字和图片,那么还需要给出第三个参数,也就是标签的水平对齐方式
– 水平对齐方式有三种:
l JLabel.LEFT:左对齐
l JLabel.CENTER:中间对齐
l JLabel.RIGHT:右对齐
l 缺省的水平对齐方式是左对齐
l 进度条
– 在传输文件或某些长时间的工作,为了及时向用户提示进度,可以使用进度条
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
– 创建进度条的步骤为:
– 建立一个JProgressBar对象,对象的构造函数中设定进度条的最小、最大值
– 设置进度条的当前值,刷新进度条
l 工具提示
– 工具提示是JComponent组件的一种性质
– 对于设置了工具提示的组件,当用户将鼠标在组件上停留一两秒之后,就会出现有关该组件功能的简单说明
l 调色板
– 调色板是一种特殊的面板,它提供了全面的颜色选择工具
JColorChooser tcc = new JColorChooser(new Color(128, 255, 128));
getContentPane().add(tcc, BorderLayout.CENTER);
– 调用调色板步骤:
l 新建一个JColorChooser对象,构造函数的参数表示当前选中的颜色
l 将调色板控件放到面板中
l 文件选择框
– 文件选择框的作用是弹出一个标准的文件或文件夹选择框,让用户从中选择文件或文件夹,以进行文件的打开、保存等工作
final JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(frame);
– 显示文件选择框的步骤:
l 创建一个JFileChooser对象
l 调用此对象的showOpenDialog函数
– 用户选择文件之后,showOpenDialog函数返回APPROVE_OPTION值
– 通过fc对象的getSelectedFile函数获得文件,如下面的代码所示:
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
System.out.println("打开文件:" + file.getName());
} else {
System.out.println("用户取消了打开文件的操作。");
}
l 文本框
– 建立文本框控件的步骤是:首先创建一个文本框对象,再把对象放到面板:
JTextField text1 = new JTextField(30);
panel.add(text1);
– 文本框构造函数中的参数是文本框中可以显示的字符长度
– 显示的字符长度不代表可以输入的字符串长度,可输入的字符长度远比显示的长度长
– 文本框对象的getText函数可以获得用户输入的文字内容,如下面的代码:
System.out.println("您的通讯地址是:" + text1.getText());
– 点击“确定”按钮,在屏幕上显示文本框中的文字内容如下:
– 文本框也可以设置动作监听器,当用户在文本框中输入完文字,按回车键时,就会触发动作监听器
– 例题:用户在文本框中输入一行文字,按回车以后,文字自动出现在文本区域中:
text1 = new JTextField(30);
text1.addActionListener(new TextActionListenerClass(this));
panel1.add(text1);
l 文本区域
– 文本区域是一块区域,用来显示和修改多行的纯文本,一般都带有滚动条。文本区域一般都放在滚动条面板中,这点类似于列表框。下面的代码创建文本区域:
JTextArea textArea1 = new JTextArea(5, 30);
JScrollPane scrollPanel = new JScrollPane(textArea1,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
– 第一行代码创建了一个文本区域,它的构造函数带有两个参数,分别表示显示的文本行数和一行中的字符数
– 第二行代码将文本区域放到一个滚动条面板中,这个滚动条面板的后两个参数表示面板的横滚条和竖滚条始终存在
– 文本区域的每行字符数随着默认字体的不同而不同,不同字体下,不同字母的宽度也不同,因此每行显示30个字符只是约数
– 如果要精确设定文本区域的大小,可以通过设置滚动条面板的大小来完成
– 文本区域的常用属性包括:
l 设置文本区域是否可以编辑(setEditable)
l 设置字体(setFont)
l 设置是否自动换行(setLineWrap)
l 设置是否在单词之间换行(setWrapStyleWord)
– setEditable:设置文本区域是否可编辑。参数为true表示可编辑,参数为false表示不可编辑
– setFont:设置字体、字号和风格:
textArea1.setFont(new Font("隶书", Font.BOLD, 24));
– setLineWrap:设置文字是否自动换行,参数为true表示自动换行,false表示不自动换行
– setWrapStyleWord:设置是在单词之间换行,还是在字符间换行。参数为true表示在单词间换行,false表示在字符间换行。这一项对中文文本基本上没有影响
– 文本区域提供的用于文本读写的函数:
l 读取全部文本(getText)
l 写入全部文本(setText)
l 添加文本(append)
l 插入文本(insert)
l 替换选定区域内的文字(replaceRange)
l 设置光标的位置(setCaretPosition)
l 选择全部文本(selectAll)
l 密码框
– 密码框的作用是让用户输入密码。为了安全,用户输入的密码不会在框内显示出来,而代之以掩码“***”。密码框常用在登录界面,
– 创建密码框的步骤为:先创建一个密码框对象,再将密码框放到面板上:
JPasswordField userPassword = new JPasswordField(20);
userPassword.setEchoChar('*');
panel.add(userPassword);
– 第二行代码的作用是设置掩码字符,缺省的掩码是“*”,就是说用户输入的所有字符都会用“*”号表示。为保险起见,在创建密码框之后应立即设置其掩码
– 从安全的角度考虑,密码框采用getPassword函数读取,返回值是字符数组。下面的代码显示了如何读取并验证输入的密码:
char[] inputPass = userPassword.getPassword();
boolean passResult = isPasswordCorrect(userName, inputPass);
for (int i=0; i<inputPass.length; i++)
input[i] = 0;
if (passResult) { ... }
else { ... }
– getPassword函数返回的是一个字符数组,其中保存着密码
– 之后通过一个函数对输入的用户名和密码进行判断,以验证密码是否正确
– 不论密码是否正确,验证完后应立即将密码数组中的每一项都置0,销毁密码
– 如果以String对象的形式存储密码,由于String对象是只读的,密码会一直以明码的形式保存在内存中,危险性较大
– 密码是十分重要且敏感的信息,程序中对密码的处理应当慎重
– 总的来说,应当遵循“出现次数尽量少,用完立刻销毁”的原则
– 原则上,正确的密码用任何函数都不能读出,只能用函数传入密码进行验证
– 如果条件允许,最好将密码用不可逆算法加密存储,从而确保密码不会泄漏
l 格式文本框
– 格式文本框能够预先设定输入文本的格式,如金额、百分数、日期等。格式文本框往往用在比较专业的方面,例如财务数据录入、科学计算等。
l 编辑面板与文本面板
– 编辑与文本面板是最复杂的文本组件:
– 它们能够以不同大小、不同字体、不同格式显示文本,甚至能够在文本中插入图片
– 编辑面板与文本面板直接支持HTML格式的文件以及RTF文件
l 文本组件类的通用特性
– 文本组件类是通用的文本处理类,它提供了文档-视结构、快捷键支持、无限制的撤销/重做等功能可供子类使用

第十四章 布局与事件驱动
本章要点
l 布局管理器的种类
– 边界型布局(BorderLayout)
– 盒式布局(BoxLayout)
– 卡片式布局(CardLayout)
– 流式布局(FlowLayout)
– 表格型布局(GridLayout)
– 表格包型布局(GridBagLayout)
– 弹性布局(SpringLayout)
l 边界型布局(BorderLayout)
– 边界型布局是框架或对话框的内容窗格缺省的布局形式
– 边界型对话框包括五个区域:北区、南区、西区、东区、中间区
– 在采用边界型布局的内容窗格中添加组件时,需要在add函数中添加一项参数,指示组件放在窗格的哪个区
– 典型的边界型界面:
aPanel.add(new JButton("北区按钮"), BorderLayout.NORTH);
aPanel.add(new JButton("南区按钮"), BorderLayout.SOUTH);
aPanel.add(new JButton("西区按钮"), BorderLayout.WEST);
aPanel.add(new JButton("东区按钮"), BorderLayout.EAST);
– 边界型布局的每个区中只能放一个组件,如果在同一个区内放入第二个组件的话,后者就会覆盖前者
– 边界型布局的中间区域是调整区,当主窗口变化时,中间区域会随之改变:
– 实际上,常用的是上下排列,或者左右排列两个组件的情况。
– 下图显示上下排列的边界型布局和左右排列的边界型布局在调整高度和宽度时的表现
– 缺省条件下,框架的内容窗格是边界型布局
– 如果我们在内容窗格中添加面板时使用缺省的add函数,面板会放在中间区
– 由于东南西北四个区都没有组件,因此中间区占据了所有的面积
– 由由于中间区是调整区,当我们调整框架大小时,面板也会自动调整到同样大小
– 从而保证了面板随时都占据框架的所有内容区域
– 我们也可以在内容窗格的顶头放置工具栏,底部放置状态栏
– 工具栏和状态栏的高度都是不变的,但长度随框架大小而改变
– 这正好是边界型布局的北区、南区的特点
– 因此,北区和南区可以放置工具栏和状态栏,同时会根据框架的大小自动调整面板、工具栏和状态栏的大小
– 用途2:用于文字编辑器界面中
– 特点:界面中最大的一块区域作为主工作区,主工作区四周是辅助工具区(可以放置按钮、提示、选项等等)
– 主工作区的大小是可变的,用户最大化窗口时就可以得到最大的主工作区空间
– 辅助工具区的大小是固定的,因为其中的按钮、提示是固定的,不需要扩大
– 对于此类界面,可以将主工作区放在中间区里,辅助工具区放在周边的东、南、西、北四个区域的某一个区域中:
– 从JDK 1.4版开始,四个区域更名:
l 北区:页首(PAGE_START)
l 南区:页尾(PAGE_END)
l 西区:行首(LINE_START)
l 东区:行尾(LINE_END)
– 中间区仍然是“CENTER”
– 原有的四区名称仍然可以使用
– 更改目的是出于国际化的考虑,适应自右向左的文字排列方式,或者竖排方式
– 缺省情况下,边界型布局的各区之间没有空隙,组件彼此靠在一起
– 可以指定区域间的水平间隙和垂直间隙:
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout(5, 5));
l 盒式布局(BoxLayout)
– 盒式布局将组件排成单个一行或一列,下面的图显示的是将5个按钮排成一行或一列:
– 对应的源代码:
// 设置横排的盒式布局
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
panel.add(new JButton("新建文件"));
panel.add(new JButton("打开"));
panel.add(new JButton("另存为"));
panel.add(new JButton("关闭"));
panel.add(new JButton("退出程序"));
// 设置竖排的盒式布局
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
– BoxLayout类的构造函数形式
– 它没有空构造函数,必须给出两个参数:
l 需要它管理的容器对象
l 组件排列的轴线方向
– 轴线方向有两种:
l LINE_AXIS(或者X_AXIS)表示组件按横线排成一行;
l PAGE_AXIS(或者Y_AXIS)表示组件按纵向排成一列
– 组件的对齐方式(Alignment):
– 对于纵向排列的组件,有3种对齐方式:
l 左对齐(LEFT_ALIGNMENT)
l 中间对齐(CENTER_ALIGNMENT)
l 右对齐(RIGHT_ALIGNMENT)
– 对齐方式是组件本身的性质,而不是布局管理器的性质。因此,你需要调用每个组件本身的setAlignmentX函数来设定对齐方式
btn1.setAlignmentX(Component.LEFT_ALIGNMENT);
btn1.setAlignmentX(Component.CENTER_ALIGNMENT);
btn1.setAlignmentX(Component.RIGHT_ALIGNMENT);
– 左对齐、中间对齐和右对齐的程序界面:
– 盒式布局的问题:组件之间没有间隙,一个个紧挨着摆放,显得很局促
– 解决的办法:
l 给每个组件设定空白边框
l 插入看不见的组件来占据空间
– 常见的不可见组件有3种:刚性区域、胶状区域和自定义区域,如下表所示:
– 为了说明不可见组件的作用,我们通过一个界面来演示。这个界面的最初外形如下:
– 这个界面的问题在于:“确定”和“取消”按钮之间离得太近
– 为解决此问题,我们在这两个按钮之间插入一块固定宽度的刚性区域空白组件,以分隔两个组件:

panel.add(new JButton("确定"));
panel.add(Box.createRigidArea(new Dimension(15,0)));
panel.add(new JButton("取消"));
– 这段代码在两个按钮之间插入了一个宽度为15的空白组件,显示效果如下:
– 界面的另一个问题是“确定”和“取消”按钮一般习惯靠右放置,而不是靠左放置
– 为此,我们在“确定”按钮前放一块水平胶状区域,它的作用是尽量扩展自身,顶住两边的组件,
– 添加水平胶状区域的代码如下:
panel.add(Box.createHorizontalGlue());
panel.add(new JButton("确定"));
panel.add(Box.createRigidArea(new Dimension(15,0)));
panel.add(new JButton("取消"));
– 添加水平胶状区域以后,它就会把“确定”按钮向右顶,从而将按钮靠右放置。
– 自定义区域的作用类似于胶状区域
– 但它的最小、最大尺寸有一定的限制,不能象胶状区域那样无限制缩小或扩张
l 卡片式布局(CardLayout)
– 卡片式布局是一种非常特殊的布局方式,它所管理的不是组件,而是面板
– 采用卡片式布局的面板,会同时拥有若干个与它大小相同的子面板,但在界面上每次只显示其中一个子面板
– 如同摞在一起的卡片一样,每次只能显示最顶上的一张
– 例题:一个图形界面程序,界面的上部是一个下拉框,当下拉框选“显示按钮”时,下面显示三个按钮,当下拉框选择“显示文本框”时,按钮消失了,出现一个文本框:
– 该程序的图形界面分上下两部分,上半部分是一个下拉框,下半部分是一个采用卡片式布局的面板:

JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(comboBoxPane, BorderLayout.PAGE_START);
panel.add(theApp.cards, BorderLayout.CENTER);
frame.getContentPane().add(panel);
– 卡片式布局的面板中有两个大小相同的子面板card1和card2:
cards = new JPanel(new CardLayout());
cards.add(card1, BUTTONPANEL);
cards.add(card2, TEXTPANEL);
– 其中“BUTTONPANEL”和“TEXTPANEL”是两个字符串常量,用来表示两个子面板:
final static String BUTTONPANEL = "显示按钮";
final static String TEXTPANEL = "显示文本框";
– 子面板1中放置的是按钮:
JPanel card1 = new JPanel();
card1.add(new JButton("新建"));
card1.add(new JButton("打开"));
card1.add(new JButton("保存"));
– 子面板2中放置的是文本框:
JPanel card2 = new JPanel();
card2.add(new JTextField("文本框", 20));
– 当你想要显示某一个面板时,只要调用卡片式布局管理器的show函数,并在参数中给出卡片所对应的字符串即可
– 下面的代码会显示面板card2,因为和card2关联的字符串TEXTPANEL的内容是“显示文本框”,与show函数的第二个参数相符合:
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, "显示文本框");
– 为了让用户选择显示哪一个面板,采用了事件驱动机制
– 当用户从下拉框中选择“显示按钮”或“显示文本框”时,会自动激活相应的事件监听器
– 事件监听器调用主程序的comboItemStateChanged函数来处理事件:
public void comboItemStateChanged(ItemEvent evt) {
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, (String)evt.getItem()); }
}
– 卡片式布局与标签页面板的异同点:
– 都可以在一个有限区域内显示同样大小的多页内容
– 由于标签页面板有自己的图形界面,因此使用起来比卡片式布局简单一些
– 但标签页面板的灵活性不如卡片式布局高
– 可以根据自己的实际需要来选择标签页面板或卡片式布局
l 流式布局(FlowLayout)
– 流式布局管理器提供一种简单的布局方式:
– 将组件排成一行,每个组件按其最适合的大小放置,如果容器的宽度不足以放下所有组件,则分成多行放
– 流式布局管理器是面板(JPanel)的缺省布局管理器
– 流式布局的构造函数有3种形式:
public FlowLayout()
public FlowLayout(int alignment)
public FlowLayout(int alignment, int horizontalGap, int verticalGap)
– 第一种构造函数是按缺省形式布局
– 第二种构造函数可以指定排列对齐方式:
l 左对齐(FlowLayout.LEADING)
l 居中对齐(FlowLayout.CENTER)
l 靠右对齐(FlowLayout. TRAILING)
– 为面板设置左对齐的流式布局排列:
panel.setLayout(new FlowLayout(FlowLayout.LEADING));
– 为面板设置右对齐的流式布局排列:
panel.setLayout(new FlowLayout(FlowLayout.TRAILING));
– 第三种构造函数形式不但可以指定排列方式,还可以指定组件间的间隙大小:
panel.setLayout(new FlowLayout(FlowLayout.LEADING, 15, 15));
l 表格型布局(GridLayout)
– 表格型布局管理器将容器变成一个表格,其中每一格的大小是完全相同的
– 每个组件都放在其中一格中
– 如果你拉大框架,你会发现每一格和格里的组件也会相应变大
– 为panel面板设定一个表格型布局管理器:
panel.setLayout(new GridLayout(0, 2));
– 构造函数中需要指定表格的行数和列数:
panel.setLayout(new GridLayout(0, 2));
panel.setLayout(new GridLayout(2, 0));
panel.setLayout(new GridLayout(3, 2));
– 第一种构造函数设定表格型布局有2列,行数不限(0表示不限)
– 第二种构造函数设定为2行,列数不限
– 第三种构造函数设定为3行2列
– 在构造函数中,行、列数至少指定一个
– 但一般不鼓励同时指定行、列数
– 因为一旦添加的组件太多或太少,Java会调整表格的行列数以符合组件数量,这样可能导致产生的界面与设想的界面完全不同
– 如果只指定行数或列数,出现问题的可能性就小得多了
– 在构造函数中还可以设定组件的横向、纵向间隙(缺省的间隙是0)。函数形式如下:
public GridLayout(int rows, int columns, int horizontalGap, int verticalGap)
– 下面的例子将组件间的间隙设为5:
panel.setLayout(new GridLayout(0, 2, 5, 5));
– 表格包型布局是最灵活、最复杂的布局之一
– 它比较接近表格型布局,但远比后者灵活,你可以定制每一格的大小、间隙等
– 表格包型布局的掌握有一定难度
– 表格包型布局的灵活性主要体现在它可以对每个单元格、每个组件进行配置
– 它需要表格包约束(GridBagConstraints)类的辅助。这个约束类的作用只有一个,就是配置每个单元格里的组件
– 下面的代码用于在表格包中加入组件:
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
panel.add(component, c);
– 代码的第一行设置布局管理器为表格包型
– 第二行代码产生一个约束对象
– 随后对约束对象进行配置
– 最后调用add函数添加组件,add函数的第二个参数为约束对象c,组件的配置信息就存储在约束对象c里
– 下面针对每个组件的约束c进行分析
– “新建文件”按钮有4个约束: button = new JButton("新建文件");
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0.5;
panel.add(button, c);
– “fill”:指示当组件的显示区域大于组件尺寸时,应当怎么办,缺省值是NONE,也就是说组件大小不变
– 其它值有HORIZONTAL(宽度),表示将组件拉宽到与单元格宽度一致,但高度不变
– VERTICAL(高度),表示将组件高度提升到与单元格高度一致,但宽度不变
– BOTH(同时),表示将组件的高度和宽度都拉伸到与单元格一致
– 本例中,这个约束是对所有组件起作用的
– “gridx”,“gridy”:组件所在的单元格位置
– 例如:gridx = 0, gridy = 0,表示组件位于左上角的单元格
– gridx = 1, gridy = 2表示组件位于第2列、第3行的单元格
– 建议对每个组件都指定它所在的单元格
– “weightx”,“weighty”:确定当用户拉大主程序界面时,各单元格如何分配多余的空间
– 这通过单元格的权重(weight)来确定
– 当用户拉大了窗口的宽度时,权重为0的单元格的宽度不变,其它权重大于0的单元格按比例分配多出来的宽度
– weighty用于设置单元格高度的权重
– 权重的绝对值不重要,关键是其相对比例
– 3个按钮所在单元格的权重都是0.5时:



– 第3个按钮所在单元格的权重为0时:
– 第4个按钮的约束如下:
c.gridx = 0;
c.gridy = 1;
c.weightx = 0.0;
c.gridwidth = 3;
c.ipady = 40;
panel.add(button, c);
– 其中引入了两个新的约束“gridwidth”和“ipady”,下面介绍这两个约束
– “gridwidth”,“gridheight”:分别表示这个组件在宽度和高度上分别占用了几个单元格
– 变量的值表示单元格个数,而不是象素数
– 本例中,由于第四个按钮很长,一个单元格放不下,因此设gridwidth = 3
– 也就是横向占据3个单元格的位置,但纵向仍然只占据了一个单元格的位置
– “ipadx”,“ipady”:设定组件内部的填充宽度或高度
– 由于填充是针对两边的,因此组件的高度至少应为它的最小高度加上ipady*2
– 本例中,为了提高第4个按钮的高度,同时将字体居中,设置它的内部填充高度为40
– 第5个按钮的约束如下:
c.gridx = 1;
c.gridy = 2;
c.gridwidth = 2;
c.ipady = 0;
c.weighty = 1.0;
c.insets = new Insets(10,0,0,0);
c.anchor = GridBagConstraints.PAGE_END;
– 其中又引入了两个新的约束“insets”和“anchor”,下面分别介绍
– “insets”:定义组件的外部填充,也就是说,在组件和单元格之间的空隙是多少
– 。外部填充值是通过一个Insets对象来指定的,它的构造函数的4个参数分别是组件顶部、左侧、底部、右侧的填充值
c.insets = new Insets(10,0,0,0);
– 本例中,指定第5个按钮的顶部与单元格之间有10个象素的空隙
– “anchor”:设定当组件小于单元格时,应该怎样放置单元格。组件有9种放置方式:
l FIRST_LINE_START(左上角),
l PAGE_START(上部的中间),
l FIRST_LINE_END(右上角),
l LINE_START(左侧的中间),
l CENTER(正中间),
l LINE_END(右侧的中间),
l LAST_LINE_START(左下角),
l PAGE_END(下部的中间),
l LAST_LINE_END(右下角)
– 在添加组件约束的时候,如果每个组件都共用一个约束,那么要注意还原设定值
– 例如第四个按钮设定ipady为40,但第五个按钮不需要设置内部填充
– 因此在定义第五个按钮的约束时要将ipady设为0,否则前面定义的约束会一直传给后面的组件
– 相反,本例中fill属性就从头传到尾,为所有单元格所用
l 弹性布局(SpringLayout)
– 从JDK 1.4版才加入的一种布局管理器
– 是一种非常灵活的布局管理器
– 它通过约束来限定组件和容器之间、组件和组件之间的位置关系
– 约束可以随意添加,因此用弹性布局可以实现普通布局管理器无法实现的复杂布局
– 另一方面,对于简单的布局,用弹性布局管理器却又显得颇为繁琐,不方便使用
– 例题:带有文字标签和文本框的简单界面:
SpringLayout layout = new SpringLayout();
panel.setLayout(layout);
JLabel label = new JLabel("请输入书名:");
panel.add(label);
JTextField text = new JTextField("书名", 15);
panel.add(text);
– 用鼠标拉开框架,文本框原来在这里:
– 但还是有问题,文字标签和文本框覆盖在一起了
– 出现这些问题的原因在于我们还没有在布局中添加约束,因此布局管理器不知道应该怎么管理这些组件
– 添加约束来反映组件和容器间的关系:
layout.putConstraint(SpringLayout.WEST, label, 5,
SpringLayout.WEST, panel);
layout.putConstraint(SpringLayout.NORTH, label, 5,
SpringLayout.NORTH, panel);
layout.putConstraint(SpringLayout.WEST, text, 5,
SpringLayout.EAST, label);
layout.putConstraint(SpringLayout.NORTH, text, 5,
SpringLayout.NORTH, panel);
– 约束1:文字标签label的左边界(WEST)应当与面板panel的左边界距离5个象素;
– 约束2:文字标签label的上边界(NORTH)应当与面板panel的上边界距离5个象素;
– 约束3:文本框text的左边界应当与文字标签的右边界(EAST)相距5个象素;
– 约束4:文本框text的上边界应当与面板panel的上边界相距5个象素
– 加入这4条约束以后,再次编译运行程序,出现的仍然是只有标题栏的界面:
– 不过,在用鼠标拉开窗体以后,显示的内容正常了:
– 窗体没有拉开的原因是面板panel的右边界和下边界(SOUTH)的位置没有约束
– 再添加两条约束:
layout.putConstraint(SpringLayout.EAST, panel, 5,
SpringLayout.EAST, text);
layout.putConstraint(SpringLayout.SOUTH, panel, 5,
SpringLayout.SOUTH, text);
– 第一条约束要求面板panel的右边界与文本框text的右边界相距5个象素
– 第二条约束要求面板panel的下边界与文本框的下边界相距5个象素
– 添加这两条约束之后,运行的程序界面终于正常了:
– 可见,弹性布局要求对每个组件,包括容器本身都指定边界约束,为保证正常显示,每个组件至少要有两个约束
– 由于约束可以由人随意定义,因此布局可以做得很灵活
– 但弹性布局要求的边界约束太多,编写这些约束是非常繁琐的工作
– 因此,比较好的办法是将弹性布局与其它布局联合起来使用
– 在少数灵活、不规则布局的地方使用弹性布局,而在大部分规则排布的地方仍然使用传统的布局管理器
– 没有布局管理器
– 一个容器也可以没有布局管理器,此时,需要手动指定组件的位置和大小
– 在使用JBuilder搭建用户界面时,没有布局管理器是最方便的
– 当我们拖放一个组件时,它会老老实实地停在想要的位置,而不会被布局管理器挪到其它地方去
– 但这样失去了界面的缩放性,它只能固定为我们设定它的大小
– 在高分辨率下设计的程序界面,拿到低分辨的系统下运行,界面很可能超出桌面
– 界面超出部分无法看到,也无法使用
– 因此,我们建议,用Swing设计任何图形界面程序都应当使用面板和布局管理器
– 考虑好在不同分辨率、不同界面大小情况下的布局
事件驱动
l 编写事件驱动的要点
– 事件可以分为两类:低级事件和语义事件
– 低级事件包括图形操作系统发生的事件,或者低级的输入事件
– 例如:移动鼠标、按下鼠标键、放开鼠标键、按下一个按键、放开一个按键等等
– 语义事件:例如用户点击按钮,或者用户在文本框中输入文字之后按回车,或者用户在组合框中选择了一项等等
– 编程时应尽可能使用语义事件而不是低级事件,以提高程序的鲁棒性和跨平台性
– 例如:监听按钮的动作事件,而不要监听鼠标按下、放开事件,因为用户除了用鼠标点击按钮之外,还可以利用键盘点击按钮
– 下拉框一定要使用语义事件,因为下拉框是由两个组件组合而成的,而不同操作系统平台下的组合可能不一样,使用语义事件就不会出现兼容性的问题
– 三种事件驱动类:普通类,内部类,匿名类
– 第一种,采用普通类作为事件驱动类:
class ButtonActionListenerClass implements ActionListener {
JFrame frame;
public ButtonActionListenerClass(JFrame frame) {
this.frame = frame; }
public void actionPerformed(ActionEvent e) {
frame.ButtonActionPerformed(e); }
}
– 第二种,采用内部类作为事件驱动类:
public class MyFrame extends JFrame {
public static void main(String[] args) {
button1.addActionListener(new MyActionListener());
}
private class MyActionListener extends ActionListener {
public void actionPerformed(ActionEvent e) {
. . . }
}
}
– 第三种,采用匿名类作为事件驱动类:
public class MyFrame extends JFrame {
public static void main(String[] args) {
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
. . . }
});
. . .
}
}
– 事件对象(EventObject):事件处理函数的参数,存储着事件发生的信息
– 在事件处理的过程中,往往需要知道是哪个组件触发了事件,这可以通过事件对象的getSource函数获得
– getSource函数返回的是最通用的Object类型的对象,可以通过变量类型转换将返回变量转换成需要的类型
– 动作事件对象还可以返回动作命令,不同的组件可以设置不同的动作命令以区分:
menuItem.addActionListener(new MenuActionListenerClass(this));
menuItem.setActionCommand("打开文件");
menuItem.addActionListener(new MenuActionListenerClass(this));
menuItem.setActionCommand("关闭文件");
public void ButtonActionPerformed(ActionEvent e) {
if ("打开文件".equals(e.getActionCommand())) { . . . }
else if ("关闭文件".equals(e.getActionCommand())) { . . . }
}
– 一个监听器接口可能包括不止一个函数
– 例:鼠标监听器就包括五个函数
– 一般监听器类只关心其中一两个函数,但Java语法要求类必须实现接口的所有函数
– 这样你的鼠标监听器类就必须有五个函数,但其中的大部分函数是空的
– 从而增加了程序的复杂性
– Java提供了适配器类,它们已经实现了接口的所有函数,但实现的函数都是空函数
– 你的监听器类只需要继承适配器类,并用你所关心的函数覆盖适配器类的函数即可
– 例:鼠标监听器的适配器类为,上面的代码可以用适配器实现如下:
public class MyMouseListenerClass extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
// 具体的函数实现代码 } }
l 常用的事件监听器
– Swing中有20多个事件监听类,包括低级事件监听类和语义事件监听类
– 有的功能比较简单,有的功能较为复杂,有些还提供适配器类
– Swing中的组件支持的事件也各不相同
– 例,按钮(单选、复选框)支持的事件:
l 动作事件(Action Listener)
l 光标事件(Caret Listener)
l 选项事件(Item Listener)
– 所有Swing组件都支持的六类监听器:
l 组件监听器:监听组件的大小、位置等的变化;
l 键盘焦点监听器:监听组件是否接收或失去了键盘输入焦点;
l 按键监听器:监听用户按下按键的事件;
l 鼠标监听器:监听鼠标点击、移入、移出事件;
l 鼠标移动监听器:监听鼠标移动事件;
l 鼠标滚轮监听器:监听鼠标滚轮滚动的事件;
l 动作监听器
– 动作监听器是最常用的监听器,很多组件都支持动作监听器
– 引起动作监听器的操作:
l 点击按钮、单选框、复选框,菜单项
l 从下拉框中选择了一项
l 在文本框、密码框中输入文字并按回车
– 进行上述操作后,组件会给所有注册过的动作监听器发送“actionPerformed”消息
– 动作监听器类是实现了ActionListener接口的类,接口中唯一的函数是actionPerformed函数。典型的动作监听器类的形式如下:
public class SampleActionListenerClass implements ActionListener {
JFrame frame;
public SampleActionListenerClass(JFrame frame) {
this.frame = frame; }
public void actionPerformed(ActionEvent e) {
frame.actionPerformed(e); }
}
l 动作监听器
– 对于简单的动作处理,可以采用内部类甚至匿名内部类来实现动作监听器类
– 组件通过addActionListener函数来加入动作监听器,如下面的代码所示:
button1.addActionListener(new SampleActionListenerClass(frame));
– 动作监听器是按钮和菜单项的必备
– 一个程序中往往有很多按钮和菜单项,这时可用命令机制,在同一个动作监听器类中利用不同的命令区分来源
– 利用组件的setActionCommand函数设置命令,利用动作事件对象的getActionCommand函数来得到命令:
String buttonCommand = e.getActionCommand();
– 你可以在动作处理函数中区分不同的命令,再执行不同的子程序,如下所示:
if ("确定".equals(buttonCommand))
frame.buttonOKClicked();
if ("取消".equals(buttonCommand))
frame.buttonCancelClicked();
l 选项监听器
– 选项监听器用于单选框、复选框,下拉框
– 用于监听选项选中与否
– 例如:用户选中复选框时,会激发一个选项监听事件
– 用户在下拉框中选择了另一项,那么会激发两个选项监听事件:
l 原来的选项不再选中
l 新的选项选中
l 选项监听器
– 选项监听器类实现ItemListener接口,这个接口只有一个函数:itemStateChanged:
public class SampleItemListenerClass implements ItemListener {
JFrame frame;
public SampleItemListenerClass(JFrame frame) {
this.frame = frame; }
public void itemStateChanged(ItemEvent e) {
frame.itemStateChanged(e); }
}
– addItemListener函数为组件添加选项监听器
– 对于下拉框来说,只需要给整个下拉框对象添加一个选项监听器就可以了
– 对于单选框与复选框,必须给每个独立的选项添加选项监听器
– 选项事件(ItemEvent)的两个函数:
l getItem函数,用来得到激发事件的选项对象
l getStateChange函数,用来判断选项的最新状态:选中或未选中
– 选项监听器与动作监听器相比,处理选项状态问题时更加专业
– 例如,对于单选框,当用户改变选项时,将会激发两个选项状态,不但通知新的选项被选中,而且通知老的选项不再选中
– 而动作监听器只针对新选中的项激发一次
– 如果用户点击的仍然是原先的单选框,没有改变选项,选项监听器不会被激发,但动作监听器仍然会激发
l 鼠标监听器
– 鼠标监听器用来监听鼠标移入、移出组件,以及鼠标键按下、放开的事件,它是一个较为复杂的监听器,接口具有5个函数
– 鼠标是应用最为频繁的工具,鼠标事件也会频繁发生,因此编写鼠标监听器程序时应当尽可能加快处理速度
– 鼠标移动事件单独在一个接口中,此类事件的处理一定要快速,否则会影响鼠标移动
– 鼠标监听器类实现MouseListener接口,这个接口有5个函数,根据接口的规范,这些函数必须全部实现:
public void mousePressed(MouseEvent e);
public void mouseReleased(MouseEvent e);
public void mouseClicked(MouseEvent e);
public void mouseEntered(MouseEvent e);
public void mouseExited(MouseEvent e);
– 如果不需要某个接口函数,可以用空函数
– 接口函数的功能:
l mousePressed函数处理鼠标键(左键或右键)按下的事件;
l mouseRelease函数处理鼠标键松开的事件;
l mouseclicked函数处理点击鼠标键(单击或双击)的事件;
l mouseEntered函数处理鼠标进入一个组件显示区域的事件;
l mouseExited函数处理鼠标移出组件显示区域的事件
– 鼠标事件对象中包含了鼠标事件的许多重要信息,常用函数如下:
int getClickCount();
int getX();
int getY();
Point getPoint();
int getButton();
boolean isPopupTrigger();
Component getComponent();
– getClickCount函数得到鼠标类型,1表示鼠标单击,2表示鼠标双击
– getX, getY, getPoint的作用都是得到鼠标在点击时的坐标
– getButton得到按下的鼠标键代码
l NOBUTTON(没有鼠标键按下)
l BUTTON1(按下鼠标键1)
l BUTTON2(按下鼠标键2)
l BUTTON3(按下鼠标键3)
– 窗口右上方是深色的鼠标功能测试区,当用户按下鼠标键时,下面的文本区域会自动显示用户按下的鼠标键名称
– 在Windows操作系统下,作为测试,我们依次按下了鼠标的左键、右键和中间键
– 文本区域分别显示对应的鼠标键的名称是BUTTON1, BUTTON3, BUTTON2
– isPopupTrigger函数用来判断刚才按下的鼠标键是不是用来显示弹出式菜单的按键
– 不同平台下,弹出菜单的鼠标键不一定相同
– 在Windows系统下,为了照顾习惯用左手的用户,也可以将鼠标的左右键对调,用左键来弹出菜单
– 不能单纯用鼠标右键判断,而应当通过isPopupTrigger函数来判断
– getComponent函数得到鼠标所在的组件
– 鼠标监听器接口有5个函数,任何实现这个接口的类必须同时实现这5个函数,这增加了程序的复杂性
– Java提供了鼠标适配器(MouseAdapter)类,鼠标监听器类可以从适配器类中继承,并实现它所需要的函数即可,其它不需要的函数可不必考虑
l 窗口监听器
– 框架和对话框支持窗口监听器,它可响应各类窗口事件,如:
l 打开窗口:表示这个窗口第一次在屏幕上显示;
l 关闭窗口:表示将窗口从屏幕上移去;
l 最小化成图标:表示将窗口缩小成一个图标,放在桌面上或工具栏里;
l 由图标还原:将窗口从图标还原成原来的大小;
l 最大化:表示将窗口扩大到允许的最大范围;
– 窗口监听器实现WindowListener接口,它有7个函数,实现它的类必须实现这7个函数
– 绝大多数窗口监听类都不直接实现WindowListener接口,而是继承窗口适配器(WindowAdapter)类,并覆盖其中的少数几个我们所关心的函数
– 常用函数有windowOpened和windowClosing函数
– windowOpened函数在窗口创建后,出现前调用,目的是完成界面显示的后台工作
– windowClosing函数在窗口关闭前调用,目的是完成窗口关闭前的工作
– 由于主框架在关闭前,一定会首先执行窗口关闭事件,因此可以将程序退出前必须做的收尾工作放在这里完成,这比响应“退出”按钮或菜单项要更加可靠
l 动作
– 使用Action(动作)对象来让多个组件实现同一项功能
– 动作对象不仅集中实现动作事件,还可以同时改变多个组件的状态
– 动作对象所能掌握的状态包括文字、图标、快捷键、以及启用(Enable)等状态
– 通过组件的setAction函数来将一个动作对象连接到该组件上
– 动作使组件发生的变化:
– (1) 组件的状态(文字、图标等)更新以反映动作对象当前的状态;
– (2) 动作对象注册为组件的ActionListener监听器;
– (3) 如果动作对象的状态发生改变,组件的状态也会随之改变;
Swing 组件的其它特性
l 使用HTML控制字体格式
– Swing组件的标准字体显示功能只能满足简单的格式要求
– 无法满足显示不同颜色、不同大小、不同字体的文字的需要
– Swing提供了一种方法,可以利用HTML的丰富的语法格式来控制字体格式
– HTML语法格式可以用在Swing的所有按钮、单选框、复选框、文字标签等组件中
– 为了使用HTML标记,只需在文本字符串的首尾分别包括“<html>”和“</html>”即可:
JLabel label3 = new JLabel(
"<html><b><u>F</u>irst</b> line:<br><b><u>S</u>econd</b> line:</html>");
– 显示的效果如下图所示:
– 也可给按钮设置字体、颜色和下划线:
JButton b1 = new JButton("<html><center><b>禁止(<u>D</u>)</b><br>"
+ "<font color=#0000ff>中间按钮</font>");
JButton b3 = new JButton("<html><center><b>允许(<u>E</u>)</b><br>"
+ "<font color=#0000ff>中间按钮</font>");
– 在屏幕上显示的效果如下:
l 组件的边框
– 每个JComponent对象以及通过JComponent继承来的组件都可以有边框
– 边框的作用:
l 为组件画出线条边框或各种花样边框
l 可以在组件周围放置标题和空白区域
– 通过setBorder函数为Swing组件设置边框
– setBorder函数的参数为一个边框对象,这个对象通过BorderFactory类来生成
– 例:为面板设置单线条边框:
JPanel panel1 = new JPanel();
panel1.setBorder(BorderFactory.createLineBorder(Color.black));
JLabel label1 = new JLabel("line border");
panel1.add(label1);
l 工具提示
– 鼠标在设置了工具提示的组件上停留一秒钟以后,界面上会自动浮现组件的功能简介
– 工具提示通过组件的setToolTipText函数来实现,如下面的例子:
JButton b2 = new JButton("中间按钮");
b2.setFont(font);
b2.setForeground(new Color(0x0000ff));
b2.setToolTipText("这是中间的按钮");
– 执行这段代码,当鼠标在中间按钮上停顿1秒钟以后,界面显示效果如下:
l 拖放支持
– Java支持拖放与剪贴板技术
– 可以在同一程序的不同组件间、两个不同的Java程序间、甚至在Java程序和其它程序之间传递数据
– 对于拖放过程,Java通过TransferHandler对象将数据传输给目标组件
– 对于剪贴板过程,同样通过TransferHandler对象数据保存到剪贴板里,再将数据取出到目标组件中
l 绑定快捷键
– JComponent类和所有由它继承而来的组件都支持快捷键绑定,步骤如下:
– 获得组件的InputMap对象,在该对象中设置快捷键的信息
– 获得组件的ActionMap对象,在该对象中设置快捷键所引发的动作事件
JButton myButton = new JButton("Button1");
myButton.getInputMap().put(KeyStroke.getKeyStroke("F2"), "doSomething");
myButton.getActionMap().put("doSomething", btnAction);
l 定时器组件
– 定时器是常用组件,它的作用是在预先设定的一段时间以后,启动动作事件
– Java中有两个定时器组件:
l Swing中的定时器组件
l java.util中的Timer类
– 两个定时器类的功能差不多。在Swing图形界面程序中,用定时器组件比较方便
– 这里只介绍Swing中的定时器组件
– 创建定时器组件的代码:
Timer timer1 = new Timer(1000, new timer1_ActionListener());
timer1.setRepeats(true);
– 第一行:创建了一个定时器对象,它的构造函数有两个参数
l 第一个参数代表定时器的延迟时间,单位以毫秒计算,1000就代表1000毫秒,也就是1秒;
l 第二个参数是动作监听器对象,当定时器到达预定时间以后,将会触发动作事件;
– 定时器有两种用法:
l (1) 执行一次,随后便停止定时器;
l (2) 反复执行定时器;
– Timer.setRepeats()函数设置重复属性
– 调用start函数来启动定时器:
timer1.start();
– 调用stop函数来中止定时器:
timer1.stop();
l 图标
– 图标就是一个具有Icon接口的对象
– 例:在按钮中添加图标:
ImageIcon icon1 = new ImageIcon("prev.gif", "向前");
ImageIcon icon2 = new ImageIcon("next.gif", "向后");
JButton btn1 = new JButton("向前", icon1);
JButton btn2 = new JButton("向后", icon2);
这段代码的显示效果如下图所示:

l 键盘输入焦点
– 组件要接受键盘操作,必须得到键盘焦点
– 在窗口显示之前,可以调用requestFocusInWindow函数来申请获得焦点,如下面的代码所示:

. . . // 框架frame的各个成员已经设置完毕,包括按钮btn1
frame.pack(); // 排列窗口中的各组件位置
btn1.requestFocusInWindow(); // 按钮btn1申请获得焦点
frame.setVisible(true); // 显示窗口
l 界面外观
– Java是跨平台系统,Java程序只需进行一次编译之后,就可以在各个平台上使用
– Java程序在各个平台上所显示的界面可能有很大差别
– Java可以设置程序的界面外观
l 可以按照操作系统特有的风格显示
l 也可以按照Java统一的风格显示
l 甚至可以设计自己的程序外观风格
– 界面风格通过UIManager.setLookAndFeel()函数设置,常见的参数如下:
– UIManager.getCrossPlatformLookAndFeelClassName():跨平台的统一界面风格;
– UIManager.getSystemLookAndFeelClassName():操作系统自己的界面风格。
l 在Windows平台下,显示Windows风格
l 在Mac OS平台下,显示Mac OS风格
l Unix/Solaris/Linux平台,显示CDE/Motif风格
– "javax.swing.plaf.metal.MetalLookAndFeel":一定要带有引号。显示Java界面风格。
– "com.sun.java.swing.plaf.windows.WindowsLookAndFeel":显示Windows界面风格。
– "com.sun.java.swing.plaf.motif.MotifLookAndFeel":显示CDE/Motif界面风格。
– "com.sun.java.swing.plaf.gtk.GTKLookAndFeel":显示GTK+界面风格。
– 同一个程序在不同界面风格下的显示结果:
– Windows风格:
– Windows XP风格和Java标准风格:
– CDE/Motif风格和GTK+风格:

第十五章 网络编程初步
网络协议入门
l 网络传输协议
– 接入网络的计算机都遵循同样的协议,这就是TCP/IP协议
– TCP/IP协议将网络分成多层
l 网络传输的层次结构
– 常用的网络层次有4层,分别是连接层、网络层、传输层和应用层
– 我们编写的网络程序通常处在应用层,在这一层,不需要追究有关网络或网卡的技术细节,就可以编写出很好的程序
基于Socket 的网络编程
l Socket类的网络编程特点
– Socket类处于应用层,无需考虑技术细节
– 利用Socket类进行网络通讯的基本步骤:
– 创建服务器端,等待连接
– 创建客户端,向服务器端发送请求
– 在客户端和服务器端建立双向连接通道
– 服务器和客户端都可以通过这条通道向对方发送消息,二者的地位是相等的
l Socket类的网络编程特点
– 在网络程序中,存在很多等待过程
l 服务器端等待客户端的连接
l 连接上的两端相互等待对方发送数据
– 程序在等待时不能响应用户的动作,使得单线程网络程序往往处于类似死机的状态
– 因此,网络程序一般都是多线程程序
l 后台线程等待网络消息
l 前台线程处理用户动作
Socket类的编程步骤
l Socket类的编程步骤1
– 建立服务器端:
– 建立服务器端对象,给出端口号,服务器端在这个端口等待客户端的连接
– 调用accept函数监听端口,程序会停止在这里,一直等待客户端的连接
– 当客户端连接到该端口之后,accept函数将建立一个Socket类,用于与客户端的通讯
– ServerSocket类会抛出IOException异常,必须用try ... catch结构捕获该异常
– 建立服务器端的代码示例:
try {
serverSocket = new ServerSocket(4444);
Socket connSocket = serverSocket.accept();
} catch (IOException e) {
System.out.println("无法监听端口4444.");
System.exit(-1);
}
l Socket类的编程步骤2
– 建立客户端的步骤:
l 创建一个Socket对象,对象构造函数的两个参数分别为服务器端的主机名以及端口号
l 程序将不断连接远程计算机,直到连接成功之后,才继续执行下面的代码
try {
clientSocket = new Socket("serverhost", 4444);
} catch (IOException e) {
System.out.println("无法与服务器建立连接。");
System.exit(-1); }
l Socket类的编程步骤3
– 建立连接之后,服务器端和客户端即可对等地利用I/O流发送或者接收信息
– 发送消息的步骤:
l 获得Socket类的输出流
l 建立一个PrintWriter对象,利用它向网络输出
l 利用println函数发送消息
PrintWriter out = new PrintWriter(
connSocket.getOutputStream(), true);
out.println("发送消息");
l Socket类的编程步骤4
– 接收消息的步骤:
l 获得Socket对象的输入流
l 新建一个读数据流的对象,以读取输入流
l 将接收到的消息保存在缓存中
l 调用缓存对象的readLine函数读入一行数据
BufferedReader in = new BufferedReader(
new InputStreamReader(
connSocket.getInputStream()));
String inStr = in.readLine();
l Socket类的编程步骤5
– BufferedReader对象是一个标准的读入、缓存数据对象。此对象缺省的缓冲区很大,足以放下收到的所有数据
– 由于不知道通过网络会收到多少数据,因此先将接收到的数据暂存起来,然后再读取
– 执行readLine函数时,会一直等到接收完一行数据之后才会继续执行下去

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics