通过 Web 服务传递二进制数据,第 5 部分:创建 Swing 客户端

本练习的目的在于为您以前创建和部署的 Web 服务创建一个客户端,然后给该客户端添加一个 GUI 界面。该界面显示 Web 服务以二进制数据传递的图像。

您可以从 NetBeans 样例目录中下载完整版本的客户端。

本教程中的课程

此页上的内容适用于 NetBeans IDE 7.2、7.3 和 7.4
  1. 概述
  2. 创建 Web 服务
  3. 对 Web 服务进行编码和测试
  4. 修改方案文件和 WSDL 文件以传递二进制数据
  5. => 创建 Swing 客户端

本课内容

  1. 创建客户端应用程序

  2. 设计 JFrame
  3. 绑定 JFrame 组件

  4. 对 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。)接受其他所有默认值,包括空的包名称。
    显示 WSDL URL 的 Web 服务客户端向导
  5. 单击 "Finish"(完成)。IDE 将下载该 WSDL 文件,添加与 Web 服务交互的客户端桩模块,并在 "Projects"(项目)窗口的 Java 应用程序项目中添加节点。
    显示新建 Web 服务客户端的项目视图

设计 JFrame 窗体

在此部分,向该 Web 应用程序添加一个 JFrame,并在其中使用 Swing 组件设计一个 GUI 界面。最后,将该 Swing 组件绑定到该 Web 服务客户端代码。

如果不想亲自设计 JFrame 窗体,可以从此处下载预先设计的 JFrame Java 文件。

  1. 右键单击 FlowerClient 节点,然后选择 "New"(新建)> "JFrame Form"(JFrame 窗体)。将该窗体命名为 FlowerFrame。并且,将其置于 flowerclient 包中。
  2. FlowerFrame 在编辑器中打开。如果 "Palette"(组件面板)尚未打开,请将其打开。扩展框架的底边框大约三分之一。
    设计视图的编辑器中的 "Flower"(鲜花)窗体,同时组件面板也已打开
  3. 将 JPanel 从 "Palette"(组件面板)的 "Swing Container"(Swing 容器)部分拖至 FlowerFrame。扩展该而板以填充整个 FlowerFrame
    在 FlowerFrame 中添加和扩展 JPanel
  4. 右键单击 "Design"(设计)视图中的 "Panel"(面板)。从上下文菜单中选择 "Change Variable Name... "(更改变量名称...)。将该面板命名为 gardenFlowersPanel
  5. 将 JLabel 从 "Palette"(组件面板)拖至 gardenFlowersPanel 的顶部。右键单击标签,然后将标签的变量名称更改为 titleLabel。再次右键单击 titleLabel 然后选择 "Edit Text"(编辑文本)。将文本更改为 "Garden Flowers"。可能希望浏览 titleLabel 的属性,并为其设置一个醒目的字体。
  6. 将 "Button Group"(按钮组)拖至设计视图中。接受按钮组的默认变量名称 buttonGroup1
  7. 将四个单选按钮拖至 titleLabel 下面的水平行中。在每个按钮的属性中,将其设置为 buttonGroup1 的成员。按钮的其他属性如下:
    buttonGroup1 中的单选按钮
    变量名称 已选定 文本
    asterRadioButton true 紫苑
    honeysuckleRadioButton false 金银花
    roseRadioButton false 玫瑰
    sunflowerRadioButton false 向日葵
  8. 将 "Scroll Pane"(滚动窗格)拖至单选按钮的下方。扩展该窗格以填充全部水平空间以及大约三分之二的空余垂直空间。将滚动窗格的变量名称更改为 mainScrollPane
  9. 将 "Panel"(面板)拖至 mainScrollPane 中。将面板的变量名称更改为 mainPanel
  10. 在 "Design"(设计)视图中,右键单击 mainPanel 然后选择 "Set Layout"(设置布局)> "Border Layout"(边框式布局)。
  11. 将 "Button"(按钮)拖至 mainPanel 中。因为 mainPanel 具有边框式布局,所以按钮将自动填充整个面板。将按钮的变量名称更改为 mainPictureButton 并将按钮的文本更改为 "Waiting for picture..."
  12. 再将一个滚动窗格拖至 mainScrollPane 下面的空间中。扩展新滚动窗格以填满全部剩余空间。将新滚动窗格的变量名称更改为 thumbnailScrollPane
  13. 将 "Panel"(面板)拖至 thumbnailScrollPane 中。将面板的变量名称更改为 thumbnailPanel。将 thumbnailPanel 的布局设置为 "Grid Layout"(网格式布局)。
  14. 将四个 "Buttons"(按钮)拖至 thumbnailPanel 中。因为 thumbnailPanel 具有网格式布局,所以按钮将自动变为相同大小并且完全填满面板。按钮的属性如下: thumbnailPanel 中的按钮
    变量名称 文本
    asterButton 正在等待...
    honeysuckleButton 正在等待...
    roseButton 等待
    sunflowerButton 正在等待...

现在 JFrame 窗体已经完全设计好了。此时,FlowerFrame 将如下所示。
显示按钮文本而不是图像的已完成 "Flower"(鲜花)框架

绑定 JFrame 组件

在此部分,将在构造函数中初始化组件,然后将这些组件绑定到监听程序。监听程序将调用用于显示鲜花图像的代码。

初始化组件

在此部分,将填充 FlowerFrame 构造函数

  1. 切换到编辑器的 "Source"(源)视图。找到 FlowerFrame 类主体的开头以及 FlowerFrame 构造函数。
    显示空 FlowerForm 构造函数的编辑器的源视图
  2. FlowerFrame 类主体的顶部,并且在构造函数之前,创建一个用于表示每种花名称的字符串数组。
    protected static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
  3. 在 FLOWERS 字符串数组和构造函数之间,添加一行代码以初始化名为 flowersjava.util.Map。该映射使用一个 String 并将其映射到某个 Image
    private Map<String, Image> flowers;
  4. java.util.Mapjava.awt.Image 添加导入语句。
  5. FlowerFrame 构造函数添加代码,以将特定的 Imageflowers 映射特定实例的特定 String 相关联
    public FlowerFrame(Map<String, Image> flowers) {
    
        this.flowers = flowers;
        for (String flower:FLOWERS) {
            flowers.put(flower,null);
        }
    
        initComponents();    
    } 
  6. 初始化单选按钮的 ItemListener 以及四个花形按钮的 ActionListener,然后设置默认标题。
    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);
    }
  7. java.awt.event.ItemListenerjava.awt.event.ActionListener 添加导入语句。

现在已完成构造函数。代码中出现了编译错误警告,这是因为代码未包含类 RBListenerButtonListener。这两个类分别是 ItemListenerActionListener 的定制实现。将在下一部分中编写这两个类。

显示鲜花

在此部分,将为单选按钮和花形按钮编写定制监听程序。还会编写一个方法,该方法用于确定按钮选择的是哪一种花,并通过 flowers 映射获取此花的 Image。最后,编写一个由 Main 类调用的方法,该方法将获取每个缩略图的 Image

  1. FlowerFrame 类主体中找到 public static void main(String args[]) 方法。删除此方法及其文档。(应用程序将改用 Main 类。)
  2. 为单选按钮编写定制 ItemListener 以代替 main 方法。当选择某个单选按钮时,该监听程序会显示新的鲜花图像。
    private class RBListener implements ItemListener {
        public void itemStateChanged(ItemEvent e) {
            showFlower();
        }
    }
  3. java.awt.event.ItemEvent 添加一条导入语句。
  4. 在定制 ItemListener 的下面,为 4 个鲜花按钮编写定制 ActionListener。当单击某个按钮时,监听程序会选择相关的单选按钮:
    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);
        }
    }
  5. java.awt.event.ActionEvent 添加一条导入语句。
  6. 在定制 ActionListener 的下面,编写 showFlower 方法。该方法用于确定哪一个单选按钮已选中并从 flowers 映射中获取相应鲜花的 Image
    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("");
    }
  7. javax.swing.ImageIcon 添加一条导入语句。
  8. 编写 setThumbnails 方法。此方法从 flowers 映射为每个缩略图获取图像。Main 类将调用此方法。
    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("");
        }
    }
  9. FlowerFrame 中修复导入(如果在代码中粘贴导入内容时未对其进行修复)。通过在编辑器中单击鼠标右键,然后从上下文菜单中选择 "Fix Imports"(修复导入),可以一次性修复所有导入内容。下面是完整的导入语句集:
    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 类。
    空主类
  2. 在类主体中的 main 方法之前,为已下载的图片数初始化一个 int 变量。
     private static int downloadedPictures;
  3. main 方法主体中,创建四种花的 HashMap 以及四张缩略图的另一个 HashMap
    final Map<String,Image> flowers = new HashMap<String,Image>(4);
    final Map<String,Image> thumbs = new HashMap<String,Image>(4);
  4. java.awt.Imagejava.util.Mapjava.util.HashMap 添加导入语句。
  5. main 方法主体中,添加代码以显示 FlowerFrame
    // Show the FlowerFrame.
    final FlowerFrame frame = new FlowerFrame(flowers);
    frame.setVisible(true);  
  6. main 方法主体中,添加代码以将客户端连接到服务。
    // The client connects to the service with this code.
    FlowerServiceService service = new FlowerServiceService();
    final FlowerService port = service.getFlowerServicePort();
  7. org.flower.service.FlowerServiceorg.flower.service.FlowerServiceService 添加导入语句。
  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.
    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();
    }
  9. org.flower.service.IOException_Exception 添加一条导入语句。
  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.
    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 && 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();
    
  11. Main.java 中修复导入(如果在代码中粘贴导入内容时未对其进行修复)。通过在编辑器中单击鼠标右键,然后从上下文菜单中选择 "Fix Imports"(修复导入),可以一次性修复所有导入内容。您可以选择要导入的 List 类;请选择 java.util.List。下面是完整的导入语句集:
    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 类现在已完成。

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 && 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 项目,然后再次运行。请注意,可以通过选择单选按钮或者单击缩略图来更改主框架中显示的图像。

要发送意见和建议、获得支持以及随时了解 NetBeans IDE Java EE 开发功能的最新开发情况,请加入 邮件列表

get support for the NetBeans

Support


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo