blob: da064fcb1d1413bfa11c67f8017dfcd27d2a11a5 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= 通过 Web 服务传递二进制数据,第 5 部分:创建 Swing 客户端
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: 通过 Web 服务传递二进制数据,第 5 部分:创建 Swing 客户端 - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, 通过 Web 服务传递二进制数据,第 5 部分:创建 Swing 客户端
本练习的目的在于为您以前创建和部署的 Web 服务创建一个客户端,然后给该客户端添加一个 GUI 界面。该界面显示 Web 服务以二进制数据传递的图像。
*本教程中的课程*
image::images/netbeans-stamp-74-73-72.png[title="此页上的内容适用于 NetBeans IDE 7.2、7.3 和 7.4"]
1. link:./flower_overview.html[+概述+]
2. link:./flower_ws.html[+创建 Web 服务+]
3. link:./flower-code-ws.html[+对 Web 服务进行编码和测试+]
4. link:./flower_wsdl_schema.html[+修改方案文件和 WSDL 文件以传递二进制数据+]
5. => 创建 Swing 客户端
*本课内容*
[start=1]
1. <<create-client-app,创建客户端应用程序>>
[start=2]
. <<design-jframe,设计 JFrame>>
[start=3]
.
<<bind-jframe,绑定 JFrame 组件>>
[start=4]
. <<code-main-class,对 Main 类进行编码>>
== 创建客户端应用程序
在此部分,将创建一个 Web 应用程序。在此应用程序内,将创建一个客户端,该客户端使用您在以前的教程中创建并修改的 Web 服务。
*创建客户端应用程序:*
1. 选择 "File"(文件)> "New Project"(新建项目)(在 Linux/Windows 上为 Ctrl-Shift-N 组合键,在 MacOS 上为 ⌘-Shift-N 组合键)。此时将打开新建项目向导。
2. 选择 "Java" 类别中的 "Java Application"Java 应用程序)选项。单击 "Next"(下一步)。此时将显示新建 Java 应用程序向导。在 "Project Name"(项目名称)中键入 ``FlowerClient`` 。选择项目的位置,然后单击 "Finish"(完成)。IDE 创建一个新的 Java 应用程序项目。
3. 右键单击 ``FlowerClient`` 项目节点,然后从上下文菜单选择 "New"(新建)> "Web Service Client"Web 服务客户端)。此时将打开新建 Web 服务客户端向导。
4. 选择 WSDL URL 单选按钮,并将 WSDL 文件的 URL 粘贴到该字段。(默认情况下,该 URL ``http://localhost:8080/FlowerAlbumService/FlowerServiceService?wsdl`` 。在浏览器中查找 URL,方法是测试 Web 服务,然后将 URL 末尾的 ``?Tester`` 替换为 ``?wsdl`` 。)接受其他所有默认值,包括空的包名称。
image::images/ws-client-wiz.png[]
[start=5]
. 单击 "Finish"(完成)。IDE 将下载该 WSDL 文件,添加与 Web 服务交互的客户端桩模块,并在 "Projects"(项目)窗口的 Java 应用程序项目中添加节点。
image::images/client-generated-sources.png[title="显示新建 Web 服务客户端的项目视图"]
[[design-jframe]]
== 设计 JFrame 窗体
在此部分,向该 Web 应用程序添加一个 ``JFrame`` ,并在其中使用 Swing 组件设计一个 GUI 界面。最后,将该 Swing 组件绑定到该 Web 服务客户端代码。
如果不想亲自设计 JFrame 窗体,可以从link:https://netbeans.org/projects/www/downloads/download/webservices%252FFlowerFrame.java[+此处+]下载预先设计的 JFrame Java 文件。
1. 右键单击 ``FlowerClient`` 节点,然后选择 "New"(新建)> "JFrame Form"JFrame 窗体)。将该窗体命名为 ``FlowerFrame`` 。并且,将其置于 ``flowerclient`` 包中。
2. ``FlowerFrame`` 在编辑器中打开。如果 "Palette"(组件面板)尚未打开,请将其打开。扩展框架的底边框大约三分之一。
image::images/opened-flowerform.png[]
[start=3]
. JPanel "Palette"(组件面板)的 "Swing Container"Swing 容器)部分拖至 ``FlowerFrame`` 。扩展该而板以填充整个 ``FlowerFrame``
image::images/add-panel.png[]
[start=4]
. 右键单击 "Design"(设计)视图中的 "Panel"(面板)。从上下文菜单中选择 "Change Variable Name... "(更改变量名称...)。将该面板命名为 ``gardenFlowersPanel``
[start=5]
. JLabel "Palette"(组件面板)拖至 ``gardenFlowersPanel`` 的顶部。右键单击标签,然后将标签的变量名称更改为 ``titleLabel`` 。再次右键单击 ``titleLabel`` 然后选择 "Edit Text"(编辑文本)。将文本更改为 "Garden Flowers"。可能希望浏览 ``titleLabel`` 的属性,并为其设置一个醒目的字体。
[start=6]
. "Button Group"(按钮组)拖至设计视图中。接受按钮组的默认变量名称 ``buttonGroup1``
[start=7]
. 将四个单选按钮拖至 ``titleLabel`` 下面的水平行中。在每个按钮的属性中,将其设置为 ``buttonGroup1`` 的成员。按钮的其他属性如下:
|===
|buttonGroup1 中的单选按钮
|变量名称 |已选定 |文本
|asterRadioButton |true |紫苑
|honeysuckleRadioButton |false |金银花
|roseRadioButton |false |玫瑰
|sunflowerRadioButton |false |向日葵
|===
[start=8]
. "Scroll Pane"(滚动窗格)拖至单选按钮的下方。扩展该窗格以填充全部水平空间以及大约三分之二的空余垂直空间。将滚动窗格的变量名称更改为 ``mainScrollPane``
[start=9]
. "Panel"(面板)拖至 ``mainScrollPane`` 中。将面板的变量名称更改为 ``mainPanel``
[start=10]
. "Design"(设计)视图中,右键单击 ``mainPanel`` 然后选择 "Set Layout"(设置布局)> "Border Layout"(边框式布局)。
[start=11]
. "Button"(按钮)拖至 ``mainPanel`` 中。因为 ``mainPanel`` 具有边框式布局,所以按钮将自动填充整个面板。将按钮的变量名称更改为 ``mainPictureButton`` 并将按钮的文本更改为 "Waiting for picture..."
[start=12]
. 再将一个滚动窗格拖至 ``mainScrollPane`` 下面的空间中。扩展新滚动窗格以填满全部剩余空间。将新滚动窗格的变量名称更改为 ``thumbnailScrollPane``
[start=13]
. "Panel"(面板)拖至 ``thumbnailScrollPane`` 中。将面板的变量名称更改为 ``thumbnailPanel`` 。将 ``thumbnailPanel`` 的布局设置为 "Grid Layout"(网格式布局)。
[start=14]
. 将四个 "Buttons"(按钮)拖至 ``thumbnailPanel`` 中。因为 ``thumbnailPanel`` 具有网格式布局,所以按钮将自动变为相同大小并且完全填满面板。按钮的属性如下: thumbnailPanel 中的按钮
|===
|变量名称 |文本
|asterButton |正在等待...
|honeysuckleButton |正在等待...
|roseButton |等待
|sunflowerButton |正在等待...
|===
现在 JFrame 窗体已经完全设计好了。此时, ``FlowerFrame`` 将如下所示。
image::images/designed-form.png[title="显示按钮文本而不是图像的已完成 "Flower"(鲜花)框架"]
== 绑定 JFrame 组件
在此部分,将在构造函数中初始化组件,然后将这些组件绑定到监听程序。监听程序将调用用于显示鲜花图像的代码。
=== [[初始化组件]]
[[在此部分,将填充 ``FlowerFrame`` 构造函数
]]
1. 切换到编辑器的 "Source"(源)视图。找到 ``FlowerFrame`` 类主体的开头以及 ``FlowerFrame`` 构造函数。
image::images/ff-empty-constructor.png[]
[start=1]
1. ``FlowerFrame`` 类主体的顶部,并且在构造函数之前,创建一个用于表示每种花名称的字符串数组。
[source,java]
----
protected static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
----
1. FLOWERS 字符串数组和构造函数之间,添加一行代码以初始化名为 ``flowers`` ``link:http://download.oracle.com/javase/6/docs/api/java/util/Map.html[+java.util.Map+]`` 。该映射使用一个 ``String`` 并将其映射到某个 ``Image`` 。
[source,java]
----
private Map<String, Image> flowers;
----
1. ``java.util.Map`` ``java.awt.Image`` 添加导入语句。
2. ``FlowerFrame`` 构造函数添加代码,以将特定的 ``Image`` ``flowers`` 映射特定实例的特定 ``String`` 相关联
[source,java]
----
public FlowerFrame(Map<String, Image> flowers) {
this.flowers = flowers;
for (String flower:FLOWERS) {
flowers.put(flower,null);
}
initComponents();
}
----
[start=3]
. 初始化单选按钮的 ``ItemListener`` 以及四个花形按钮的 ``ActionListener`` ,然后设置默认标题。
[source,java]
----
public FlowerFrame(Map<String, Image> flowers) {
this.flowers = flowers;
for (String flower:FLOWERS) {
flowers.put(flower,null);
}
initComponents();
setTitle("Garden Flowers [waiting for picture]");
ItemListener rbListener = new RBListener();
asterRadioButton.addItemListener(rbListener);
honeysuckleRadioButton.addItemListener(rbListener);
roseRadioButton.addItemListener(rbListener);
sunflowerRadioButton.addItemListener(rbListener);
ActionListener bListener = new ButtonListener();
asterButton.addActionListener(bListener);
honeysuckleButton.addActionListener(bListener);
roseButton.addActionListener(bListener);
sunflowerButton.addActionListener(bListener);
}
----
[start=4]
. ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ItemListener.html[+java.awt.event.ItemListener+]`` 和 ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ActionListener.html[+java.awt.event.ActionListener+]`` 添加导入语句。
现在已完成构造函数。代码中出现了编译错误警告,这是因为代码未包含类 ``RBListener`` ``ButtonListener`` 。这两个类分别是 ``ItemListener`` ``ActionListener`` 的定制实现。将在下一部分中编写这两个类。
=== [[显示鲜花]]
[[在此部分,将为单选按钮和花形按钮编写定制监听程序。还会编写一个方法,该方法用于确定按钮选择的是哪一种花,并通过 ``flowers`` 映射获取此花的 ``Image`` 。最后,编写一个由 ``Main`` 类调用的方法,该方法将获取每个缩略图的 ``Image``
]]
1. ``FlowerFrame`` 类主体中找到 ``public static void main(String args[])`` 方法。删除此方法及其文档。(应用程序将改用 ``Main`` 类。)
. 为单选按钮编写定制 ``ItemListener`` 以代替 ``main`` 方法。当选择某个单选按钮时,该监听程序会显示新的鲜花图像。
[source,java]
----
private class RBListener implements ItemListener {
public void itemStateChanged(ItemEvent e) {
showFlower();
}
}
----
. ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ItemEvent.html[+java.awt.event.ItemEvent+]`` 添加一条导入语句。
. 在定制 ``ItemListener`` 的下面,为 4 个鲜花按钮编写定制 ``ActionListener`` 。当单击某个按钮时,监听程序会选择相关的单选按钮:
[source,java]
----
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == asterButton) asterRadioButton.setSelected(true);
else if (e.getSource() == honeysuckleButton) honeysuckleRadioButton.setSelected(true);
else if (e.getSource() == roseButton) roseRadioButton.setSelected(true);
else if (e.getSource() == sunflowerButton) sunflowerRadioButton.setSelected(true);
}
}
----
[start=2]
. ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ActionEvent.html[+java.awt.event.ActionEvent+]`` 添加一条导入语句。
[start=3]
. 在定制 ``ActionListener`` 的下面,编写 ``showFlower`` 方法。该方法用于确定哪一个单选按钮已选中并从 ``flowers`` 映射中获取相应鲜花的 ``Image``
[source,java]
----
void showFlower() {
Image img = null;
if (asterRadioButton.isSelected()) {
img = flowers.get("aster");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Aster]");
}
} else if (honeysuckleRadioButton.isSelected()) {
img = flowers.get("honeysuckle");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Honeysuckle]");
}
} else if (roseRadioButton.isSelected()) {
img = flowers.get("rose");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Rose]");
}
} else if (sunflowerRadioButton.isSelected()) {
img = flowers.get("sunflower");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Sunflower]");
}
}
if (img == null) {
mainPictureButton.setIcon(null);
setTitle("Garden Flowers [waiting for picture]");
} else mainPictureButton.setText("");
}
----
[start=4]
. ``link:http://download.oracle.com/javase/6/docs/api/javax/swing/ImageIcon.html[+javax.swing.ImageIcon+]`` 添加一条导入语句。
[start=5]
. 编写 ``setThumbnails`` 方法。此方法从 ``flowers`` 映射为每个缩略图获取图像。 ``Main`` 类将调用此方法。
[source,java]
----
void setThumbnails(Map<String, Image> thumbs) {
Image img = thumbs.get("aster");
if (img != null) {
asterButton.setIcon(new ImageIcon(img));
asterButton.setText("");
}
img = thumbs.get("honeysuckle");
if (img != null) {
honeysuckleButton.setIcon(new ImageIcon(img));
honeysuckleButton.setText("");
}
img = thumbs.get("rose");
if (img != null) {
roseButton.setIcon(new ImageIcon(img));
roseButton.setText("");
}
img = thumbs.get("sunflower");
if (img != null) {
sunflowerButton.setIcon(new ImageIcon(img));
sunflowerButton.setText("");
}
}
----
[start=6]
. ``FlowerFrame`` 中修复导入(如果在代码中粘贴导入内容时未对其进行修复)。通过在编辑器中单击鼠标右键,然后从上下文菜单中选择 "Fix Imports"(修复导入),可以一次性修复所有导入内容。下面是完整的导入语句集:
[source,java]
----
import java.awt.Image;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.ItemEvent;import java.awt.event.ItemListener;import java.util.Map;import javax.swing.ImageIcon;
----
``FlowerFrame`` 现在已完成。
== [[对 Main 类进行编码]]
[[在此部分,将完成 ``Main`` 类,以使其显示 ``FlowerFrame`` ,连接到 Web 服务,并调用 Web 服务操作。
1. 在编辑器中打开 ``Main.java`` 类。
image::images/main-empty.png[]
[start=2]
. 在类主体中的 ``main`` 方法之前,为已下载的图片数初始化一个 ``int`` 变量。
[source,java]
----
private static int downloadedPictures;
----
[start=3]
. ``main`` 方法主体中,创建四种花的 ``HashMap`` 以及四张缩略图的另一个 ``HashMap``
[source,java]
----
final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
----
[start=4]
. ``java.awt.Image`` ``java.util.Map`` ``java.util.HashMap`` 添加导入语句。
[start=5]
. ``main`` 方法主体中,添加代码以显示 ``FlowerFrame`` 。*// Show the FlowerFrame.*
[source,java]
----
final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);
----
[start=6]
. ``main`` 方法主体中,添加代码以将客户端连接到服务。*// The client connects to the service with this code.*
[source,java]
----
FlowerServiceService service = new FlowerServiceService();
final FlowerService port = service.getFlowerServicePort();
----
[start=7]
. ``org.flower.service.FlowerService`` ``org.flower.service.FlowerServiceService`` 添加导入语句。
[start=8]
. ``main`` 方法主体中,添加代码,创建一个具有四个 ``Runnable`` 线程的数组,并在每个线程中调用一次 Web 服务的 ``getFlower`` 操作。*// The web service getFlower operation
// is called 4 times, each in a separate thread.
// When the operation finishes the picture is shown in
// a specific button.*
[source,java]
----
Runnable[] tasks = new Runnable[4];
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
*// Call the getFlower operation
// on the web service:*
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
*// Add strings to the hashmap:*
flowers.put(FlowerFrame.FLOWERS[index],img);
*// Call the showFlower operation
// on the FlowerFrame:*
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
----
[start=9]
. ``org.flower.service.IOException_Exception`` 添加一条导入语句。
[start=10]
. ``main`` 方法主体中,添加代码以在单独的线程中调用 Web 服务的 ``getThumbnails`` 操作。*// The web service getThumbnails operation is called
// in a separate thread, just after the previous four threads finish.
// When the images are downloaded, the thumbnails are shown at
// the bottom of the frame.*
[source,java]
----
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
*// Call the getThumbnails operation
// on the web service:*
List<Image> images = port.getThumbnails();
System.out.println("thumbs downloaded");
if (images != null &amp;&amp; images.size() == 4) {
for (int i=0;i<4;i++) {
thumbs.put(FlowerFrame.FLOWERS[i],images.get(i));
}
frame.setThumbnails(thumbs);
}
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
}
};
new Thread(thumbsTask).start();
----
[start=11]
. ``Main.java`` 中修复导入(如果在代码中粘贴导入内容时未对其进行修复)。通过在编辑器中单击鼠标右键,然后从上下文菜单中选择 "Fix Imports"(修复导入),可以一次性修复所有导入内容。您可以选择要导入的 List 类;请选择 ``java.util.List`` 。下面是完整的导入语句集:
[source,java]
----
import flower.album.FlowerService;import flower.album.FlowerService_Service;import flower.album.IOException_Exception;import java.awt.Image;import java.util.HashMap;import java.util.List;import java.util.Map;
----
``Main`` 类现在已完成。
[source,java]
----
public class Main {
private static int downloadedPictures;
public static void main(String[] args) {
final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
*// Show the FlowerFrame.*
final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);
* // The client connects to the service with this code.*
FlowerService_Service service = new FlowerService_Service();
final FlowerService port = service.getFlowerServicePort();
Runnable[] tasks = new Runnable[4];
*// The web service getFlower operation
// is called 4 times, each in a separate thread.
// When the operation finishes the picture is shown in
// a specific button.*
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
*// Call the getFlower operation
// on the web service:*
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
*// Add strings to the hashmap:*
flowers.put(FlowerFrame.FLOWERS[index],img);
*// Call the showFlower operation
// on the FlowerFrame:*
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
*// The web service getThumbnails operation is called
// in a separate thread, just after the previous four threads finish.
// When the images are downloaded, the thumbnails are shown at
// the bottom of the frame.*
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
*// Call the getThumbnails operation
// on the web service:*
List<Image> images = port.getThumbnails();
System.out.println("thumbs downloaded");
if (images != null &amp;&amp; images.size() == 4) {
for (int i=0;i<4;i++) {
thumbs.put(FlowerFrame.FLOWERS[i],images.get(i));
}
frame.setThumbnails(thumbs);
}
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
}
};
new Thread(thumbsTask).start();
}
}
----
现在该客户端应用程序完整了,具有与 Web 服务交互的代码,该 Web 服务委托给了 EJB 模块,以公开其图像。右键单击客户端,然后选择 "Run"(运行)。将启动 Swing 应用程序,并且之后会填充从 Web 服务接收到的图像。如果图像没有全部出现,请清理并构建 FlowerService 项目,然后再次运行。请注意,可以通过选择单选按钮或者单击缩略图来更改主框架中显示的图像。
]]
link:/about/contact_form.html?to=3&subject=Feedback:%20Flower%20Swing%20Client%20EE6[+发送有关此教程的反馈意见+]
要发送意见和建议、获得支持以及随时了解 NetBeans IDE Java EE 开发功能的最新开发情况,请link:../../../community/lists/top.html[+加入 nbj2ee@netbeans.org 邮件列表+]。