Vaadin中文开发手册

npen 贡献于2012-10-21

作者 User  创建于2011-07-15 05:59:00   修改者Microsoft  修改于2012-07-30 15:04:00字数57138

文档摘要:Vaadin简介略Vaadin入门本章将以一个实例来说明Vaddin的安装和工具使用,以及如何在Eclipse(Vaadin提供Eclipse等开发IDE的插件)下如何运行和调试。安装本章将对Vaadin包以及安装做个概述。安装有两种方式可选:下载安装包如果你使用Eclipse,可以安装Vaadin的Eclispe插件。
关键词:

Vaadin 1. 简介 略 2. Vaadin入门 本章将以一个实例来说明Vaddin的安装和工具使用,以及如何在Eclipse(Vaadin提供Eclipse等开发IDE的插件)下如何运行和调试。 2.1. 安装 本章将对Vaadin包以及安装做个概述。安装有两种方式可选: u 下载安装包 u 如果你使用Eclipse,可以安装Vaadin的Eclispe插件,“Eclipse下的Vaadin插件”章节会做详细介绍。 如果€使用Eclispe,可以安装安装包,然后导入“快速入门”项目到Eclipse下。你将可以运行并调试演示程序及包含的文档。 2.1.1. 安装发布包 按照下面简单几步完成安装: 1. 从http://vaadin.com/download/下载最新版的Vaadin安装包 2. 用你系统中的压缩软件解压安装包到任意目录下(参考下例) l 在Windows下,用默认ZIP解压程序解压安装包到你指定的目录下,如:C:\dev 警告 至少Windows XP默认的压缩软件和某些版本的WinRAR软件某些情况下无法解压安装包。解压后可能会出现类似“系统无法找到指定文件”的错误。这个错误产生的原因是由于压缩软件不支持长文件路径名,总长度超出256字符。此问题常发生在解压到桌面的时候。你可以尝试解压安装程序到C:\dev或一些短路径的目录下,再或者选择其他的压缩软件。 l 在Linux、Mac OS X和UNIX系列系统下用InfoZIP或其他ZIP程序用unzip vaadin-6.x.x.zip命令解压安装包 2.1.2. 启动内容浏览器 当你在使用Vaadin的时候,内容浏览器(Content Browser)(没用过这个软件)是你最好的朋友。它允许你浏览文档和一些示例源码,以及演示程序。演示程序演示了大部分Vaadin的主要特性。你可以从Vaadin站点:http://vaadin.com/demo找到演示程序。 启动内容浏览器,运行Vaadin安装目录中的启动脚本。这将会启动一个独立的Web服务器,运行在本地主机的8888端口,使用网页浏览器输入地址http://localhost:8888/。 内容浏览器会你打开你系统中默认的网页浏览器。如果默认的浏览器与Vaadin的演示程序不兼容导致无法正常工作。在这种情况下,需要启动一个可被支持的浏览器并访问http://localhost:8888/ 如果内容浏览器启动失败,确认一下其他的服务没有占用8888端口。 JRE必须已安装 你必须安装了Java运行环境(JRE),否则执行批处理文件将发生错误并立即关闭。JRE可以从http://java.sun.com/javase/downloads/index.jsp处下载。 防火墙软件 本机启动内容浏览器可能会导致你的防火墙软件发出安全警告,原因是启动了WEB服务器。你需要允许内容浏览器连接8888端口 Windows 双击运行start.bat批处理文件图标,等待一会,直到Web服务器和网页浏览器已启动。 Linux / UNIX 略 Mac OS X 略 浏览器支持 Vaddin支持大多数常见浏览器,包括IE6—8,Firxfox3,Sarari3和Opera9.6。除了上述浏览器外,可从http://vaadin.com/features找到浏览器的支持情况 2.1.3. 包内容 在安装根目录下,你可以找到start.bat(Windows)或start.sh(Linux和Mac)脚本文件 启动内容浏览器,你可以运行演示程序,并阅读包中含有的文档。 图1.1 包内容 WebContent目录包含Vaadin所有相关文件。如果你不希望或者不能运行内容浏览器,你可以在网页浏览器中打开index.html文件查看安装包内容和文档。虽然这些示例程序并不可用。 release-notes.html文件包含了最新发布版本所作修改的内容介绍和发布历史。License子目录包含Vaadin的授权指南(licensing-guidelines.html)及其他类库的安装授权。安装目录根下的COPYING文件也包含了授权信息。Vaadin自己的库在WebContent/vaadin-6.x.x.jar。jar中包含了完整源码以及编译后的class文件。 WebContent/docs目录下包含了全部Vaadin文档,包括JavaDoc,API相关文档(api子目录)和该手册的HTML格式及PDF格式。 WebContent/WEB-INF目录下的src子目录下包含了实例程序的源码,lib子目录下包含所需类库。 GWT目录包含了完整的Google Web Toolkit安装包,包含所选操作系统的运行库(*.dll),完整的文档和示例。如果你打算为Vaadin编译客户端组件,那么你就需要GWT。(参考“自定义组件开发”章节) 此外,安装目录包含了可以作为Eclipse项目的所需文件,可以导入到Eclipse中。参考“用Eclispe快速入门”章节如何使用 2.1.4. 示例程序 内容浏览器允许你运行安装包中的几个实例程序。示例程序将展示如何帮你用Vaadin来处理不同的任务。所有示例的源码都包含在安装包中,如果你导入了安装包到Eclipse中,你可以直接编辑这些源码。参考“用Eclispe快速入门”章节。 Sampler Sampler演示了Vaadin的各个组件使用及特性。点击某个可用示例项前的图标会带你到示例页,在每个示例页中,都能看到相关特性的简短描述,以及完整的示例代码。 (译者注:启动示例程序后实例程序地址,可以打开示例页,左侧树是所有演示的示例,点击某一个示例后,会在右侧刷新出该示例的示例效果,并可查看该示例的源码) Address Book Tutorial(通讯录教程) 该教程将教你一步一步的构建一个Vaadin程序:如何创建布局和视图,组件和数据的绑定,使用等等。该该章节还将介绍如何为应用程序创建自定义皮肤。 Reservation Application(预订系统) 预订系统程序将演示利用各个组件构建一个连接本地数据库的半真实的系统。该程序会集成GoogleMaps到程序中。 Coverflow(自己百度去) 关于如何集成Flex到Vaadin的简单示例。 VaadinTunes 非功能应用程序,演示如何利用Vaadin创建复杂布局。 2.2. 设置开发环境 2.2.1. 安装JDK 2.2.2. 安装Eclipse安装Tomcat 2.2.3. Firefox和Firebug 2.2.4. Vaadin的Eclipse插件 2.3. 用Eclipse快速入门 2.3.1. 启动Eclipse 2.3.2. 导入Vaadin项目 2.3.3. 在Eclipse中启动示例程序 2.3.4. 在Eclipse中调试示例程序 2.4. 你的第一个Vaadin项目 本章节提供说明一个用Vaadin插件创建一个新的Eclipse项目,包含以下几步: 1 创建新的项目 2 编写源码 3 配置Tomcat(或其他Web服务器) 4 打开网页浏览器用于浏览程序 我们将展示如何在Eclipse中调试应用程序。 本示例假设您已安装好Vaadin并设置好你的开发环境。 2.4.1. 创建项目 让我们用前一章节安装的工具创建第一个应用程序。首先启动Eclipse按照下面的步骤进行: 1. 创建一个新的项目,选择菜单:文件(File)->新建(New)->项目(Project…) 2. 在“新建项目”窗口弹出后,选择Web->Vaadin Project,然后点击“Next” 3. 在“Vaadin Project”步骤中,需要设置Web项目的基本配置。至少需要设置项目名称和运行时,其他选项保持默认值即可。 此处,你可以点击“Finish”完成,其他设置将采用默认值,或者点击“Next” 4. 在“Web Module”步骤定义Servlet相关的设置及Web应用程序的项目结构。所有设置项都是预先填好的,通常情况下可以采用这些设置而无需修改。如果同意这些设置,则点击“Next”。 5. 在“Vaadin project”步骤页有各种Vaadin特有的应用程序设置。如果你是第一次尝试Vaadin,你最好不要修改这些设置。除了创建Portal配置,你可以修改大多数设置。 Create project template 是否使用模板向导创建一个应用程序类存根 Application Name 显示在浏览器窗口上的标题名称 Base package name 应用程序被指定的Java包名 Application class name Vaadin应用程序类名 Create portlet configuration 如果选择该项,向导将创建一个门户应用程序所需的文件 最后,点击“Finish”创建项目 6. Eclipse可能要求切换到J2EE透视图。动态Web项目使用外部Web服务器和J2EE透视图提供了工具来控制服务器和管理应用程序部署。点击“Yes”。 2.4.2. 项目探索 “New Project”向导退出后,他将为我们做一下工作:Vaadin类库存放在WebContent/WEB-INF/lib目录下。源码存在于src目录下。WebContent/WEB-INF/web.xml中包含了部署描述符。 图 一个新的动态Web项目: 通过插件创建的Application类的代码如下: package com.example.myproject; import com.vaadin.Application; import com.vaadin.ui.*; public class MyprojectApplication extends Application { @Override public void init() { Window mainWindow = new Window("Myproject Application"); Label label = new Label("Hello Vaadin user"); mainWindow.addComponent(label); setMainWindow(mainWindow); } } 下面让我们在应用程序上加入一个按钮使之变的更有意思。在init()方法里加入如下代码: public void init() { final Window mainWindow = new Window("Myproject Application"); Label label = new Label("Hello Vaadin user"); mainWindow.addComponent(label); mainWindow.addComponent( new Button("What is the time?", new Button.ClickListener() { public void buttonClick(ClickEvent event) { mainWindow.showNotification( "The time is " + new Date()); } })); setMainWindow(mainWindow); } WebContent/WEB-INF/web.xml中定义的Vaadin框架的Servlet,Application类和Servlet-mapping 样例Web.xml部署描述符 myproject Vaadin production mode productionMode false Myproject Application com.vaadin.terminal.gwt.server.ApplicationServlet Vaadin application class to start application com.example.myproject.MyprojectApplication Myproject Application /* Web.xml更多关于部署描述符的的文章,参考章节4.8.3, “Deployment Descriptor web.xml”. 2.4.3. 设置并启动Web服务器 Eclipse IDE for Java EE Developers安装了Web标准工具包。支持各种Web服务器,并可在项目有修改时自动部署到服务器。 确认以用户权限安装了Tomcat,如果用户对于Tomcat的安装目录没有写的配置和部署权限,则在Eclipse里对Web服务器的配置也会失败 。 遵循以下步骤: 1. 切换Eclipse下面的面板选项卡到“Servers”,安装后的Eclipse的服务器面板里应该是空的,在面板的空白区域鼠标右键,然后选择“New”->“Server”。 2. 选择Apache->Tomcat v6.0 Server ,设置作为本地服务器的服务器主机名字。如果你只安装了Tomcat,Server runtime也只有一个选择。点击“Next” 3. 点击“Add”将你的项目从左侧添加到右侧的已配置项目里。点击“Finish” 4. 服务器和项目现已安装在Eclipse中,并在“Servers”选项卡上显示了已配置的服务器。要启动服务器时,只需在服务器上右键鼠标选择“Debug”,启动调试模式。如果不需要调试模式,则选择“Start”。 5. 启动服务器并将WebContent目录发布到服务器上:http://localhost:8080/myproject/. 2.4.4. 运行和调试 从上述章节创建的“myproject”应用程序启动是很简单的,以此选择“Run”->“Debug As”->“Debug on Server”。Eclipse会打开一个内置的网页浏览器。 图 运行Vaadin应用程序 你可以在源码窗口双击代码行左侧边缘条以设置断点。例如,如果你在buttonClick()方法上设置断点,点击页面中的“What is the time?”按钮,Eclipse会切换到调试透视图并在断点处停止,此时你可以修改和查看应用程序的状态。如需继续执行,从“Run”菜单选择“Resume”。 图 调试Vaadin应用程序 上述过程可用于调试服务器端程序,如果想了解如何调试客户端组件,请参考章节10.8.6, “GWT Development Mode”。 3. 结构 3.1. 概述 略 3.2. 技术背景 3.2.1. AJAX 略 3.2.2. Google Web Toolkit(GWT) 略 3.2.3. JSON 略 3.2.4. 应用程序Java Servlet的会话 略 3.2.5. 事件和监听器 略 4. 应用程序编写 4.1. 概述 略 4.2. 管理主窗口 通常“网页”只运行在一个浏览器窗口中,一般网页在被打开后不会被重载,但他会通过Ajax使远程的通信服务器与用户进行交互。一个Ajax应用程序窗口更像是一个桌面应用程序窗口而并不像一个网页。 一个“Window”在浏览器中是用户界面显示的顶级容器。Ajax应用程序运行在单一的“页面”(URL)上,通常只有一个窗口——主窗口。主窗口可以用URL进行访问,在Application类中通过setMainWindow()方法设置主窗口。 import com.vaadin.ui.*; public class HelloWorld extends com.vaadin.Application { public void init() { Window main = new Window("The Main Window"); setMainWindow(main); ... fill the main window with components ... } } 你可以使用addComponent()方法将组件添加到主窗口上,或任何其他窗口上,同时默认为窗口组件绑定了一个根布局组件,如果你希望使用其他的根布局组件,你可以通过setContent()方法进行设置。参考章节6.2, “Window and Panel Root Layout”. Vaadin有两种类型的窗口:应用程序顶级窗口(如“主窗口”)和子窗口(主窗体内的子窗口)。子窗口将在下一章节介绍,而应用程序及窗口在章节11.2, “Application-Level Windows”介绍 4.3. 子窗体 一个主窗口可以有多个浮动的子窗口,他们依靠HTML功能被Vaadin的JavaScript客户端脚本管理的。子窗口可以被打开和关闭,从一个窗口刷新另一个窗口,调整窗口的大小及滚动窗口的内容。子窗口常用于对话窗口和多文档界面程序。子窗口默认不是“模态”的,但你可将其设置为模态窗口,参考章节4.3.3, “Modal Windows”。 所有用户界面组件,窗口的外观和内容都是通过主题(themes)定义的。 对窗口的控制仅限于移动,调整大小和关闭,还暂不支持最大化和最小化。 4.3.1. 子窗体的打开和关闭 你可以通过新建一个Window对象来打开一个新的窗口,然后将其通过Application 类的addWindow()方法加入到主窗口中。 mywindow = new Window("My Window"); mainwindow.addWindow(mywindow); 可以用类似的方式关闭一个窗口,调用Application类的removeWindow()方法。 myapplication.removeWindow (mywindow); 用户在默认情况下也可以通过点击窗口右上角的关闭按钮来关闭窗口。可以通过setReadOnly(true)设置窗口为只读,以达到关闭按钮不可用的效果。提示:你可以设置CSS样式为"display: none"使关闭按钮在不可用时变的不可见。问题是可能会使有些用户恶意的重启该按钮,并关闭窗口,这样可能会导致一些安全性漏洞。设置为窗口只读并且在客户端设置关闭按钮不可用,可以防止处理在服务端的关闭事件。 下面演示了一个应用程序中使用子窗口的例子。该窗口使用一些自定义组件,包含用于打开和关闭窗口的按钮。 /** Component contains a button that allows opening a window. */ public class WindowOpener extends CustomComponent implements Window.CloseListener { Window mainwindow; // Reference to main window Window mywindow; // The window to be opened Button openbutton; // Button for opening the window Button closebutton; // A button in the window Label explanation; // A descriptive text public WindowOpener(String label, Window main) { mainwindow = main; // The component contains a button that opens the window. final VerticalLayout layout = new VerticalLayout(); openbutton = new Button("Open Window", this, "openButtonClick"); explanation = new Label("Explanation"); layout.addComponent(openbutton); layout.addComponent(explanation); setCompositionRoot(layout); } /** Handle the clicks for the two buttons. */ public void openButtonClick(Button.ClickEvent event) { /* Create a new window. */ mywindow = new Window("My Dialog"); mywindow.setPositionX(200); mywindow.setPositionY(100); /* Add the window inside the main window. */ mainwindow.addWindow(mywindow); Opening and Closing a Child Window 47 Writing a Web Application /* Listen for close events for the window. */ mywindow.addListener(this); /* Add components in the window. */ mywindow.addComponent( new Label("A text label in the window.")); closebutton = new Button("Close", this, "closeButtonClick"); mywindow.addComponent(closebutton); /* Allow opening only one window at a time. */ openbutton.setEnabled(false); explanation.setValue("Window opened"); } /** Handle Close button click and close the window. */ public void closeButtonClick(Button.ClickEvent event) { /* Windows are managed by the application object. */ mainwindow.removeWindow(mywindow); /* Return to initial state. */ openbutton.setEnabled(true); explanation.setValue("Closed with button"); } /** In case the window is closed otherwise. */ public void windowClose(CloseEvent e) { /* Return to initial state. */ openbutton.setEnabled(true); explanation.setValue("Closed with window controls"); } } 示例中的自定义组件均继承了CustomComponent类。示例由一个用于打开窗口的“Button”和一个用户描述窗口状态的“Label”组成。当窗口打开时按钮为不可用状态,当窗口关闭后,按钮再次变为可用状态。 The example implements a custom component that inherits the CustomComponent class. It 你可以安装上述实例,在程序中使用自定义组件: public void init() { Window main = new Window("The Main Window"); setMainWindow(main); addComponent(new WindowOpener("Window Opener", main)); } 当上面代码加入到一个应用程序后,屏幕截图如下所示: 图 打开一个子窗口 4.3.2. 窗体定位 窗口被创建后,将会有一个默认的位置定位,你可以通过setHeight() 和setWidth()方法指定窗口的大小,也可以使用setPositionX() 和 setPositionY()方法来设置窗口的位置 /* Create a new window. */ mywindow = new Window("My Dialog"); /* Set window size. */ mywindow.setHeight("200px"); mywindow.setWidth("400px"); /* Set window position. */ mywindow.setPositionX(200); mywindow.setPositionY(50); 注:主窗口的大小是未知的,调用getHeight() 和getWidth()方法会返回-1。 4.3.3. 模态窗口 模态窗口是一个子窗口,在子窗口关闭之前不运行对父窗口进行任何操作。对话框窗口是典型的模态框。模态框的优势在于有助于用户界面交互的简单化,从开发角度讲模态框也是很简单的,因为对于用户交互上他是相对独立的。当模态窗口打开后,会对应用程序状态有更多限制,但他的缺点是对于工作流程有了过多限制。 图 示例程序下中的模态框示例截图 根据主题(theme)设置,当模态框打开后父窗口可能会变为灰色。 安全警告 子窗口是纯粹的客户端功能,可以规避一些客户端代码攻击。不应该在子窗口中做一些相关安全的关键操作,如:作登录窗口。 4.4. 用监听器处理事件 学习完章节 3.5, “Events and Listeners”后关于事件处理的学习后,我们就要付诸于实践。你可以通过下面介绍的三中基本方法来处理事件。 下面是个典型的例子,有个“Button”和一个监听器来处理用户交互(单击)作为事件传达给应用程序。我们定义一个点击事件的监听类: public class TheButton implements Button.ClickListener { Button thebutton; /** Creates button into given container. */ public TheButton(AbstractComponentContainer container) { thebutton = new Button ("Do not push this button"); thebutton.addListener(this); container.addComponent(thebutton); } /** Handle button click events from the button. */ public void buttonClick (Button.ClickEvent event) { thebutton.setCaption ("Do not push this button again"); } } 作为经常会接收到同一个类的几个组件的事件的应用程序,例如多个按钮,必须能够区分出每个组件。有几个技术可以帮你完成这个,最简单的方式是将发送对象作为属性设置到事件中,需要手动维护每个发送对象的事件引用。 public class TheButtons implements Button.ClickListener { Button thebutton; Button secondbutton; /** Creates two buttons in given container. */ public TheButtons(AbstractComponentContainer container) { thebutton = new Button ("Do not push this button"); thebutton.addListener(this); container.addComponent(thebutton); secondbutton = new Button ("I am a button too"); secondbutton.addListener(this); container.addComponent (secondbutton); } /** Handle button click events from the two buttons. */ public void buttonClick (Button.ClickEvent event) { if (event.getButton() == thebutton) thebutton.setCaption("Do not push this button again"); else if (event.getButton() == secondbutton) secondbutton.setCaption("I am not a number"); } } 另一种解决方案适用于同一个类的多个事件,这个涉及将事件源附加到监听方法而不是监听的类。 public class TheButtons implements Button.ClickListener { Button thebutton; Button secondbutton; /** Creates two buttons in given container. */ public TheButtons(AbstractComponentContainer container) { thebutton = new Button ("Do not push this button"); thebutton.addListener(this); container.addComponent(thebutton); secondbutton = new Button ("I am a button too"); secondbutton.addListener(this); container.addComponent (secondbutton); } /** Handle button click events from the two buttons. */ public void buttonClick (Button.ClickEvent event) { if (event.getButton() == thebutton) thebutton.setCaption("Do not push this button again"); else if (event.getButton() == secondbutton) secondbutton.setCaption("I am not a number"); } } 事件可以使用其中一个带有一个参数的addListener()方法,参数即是用一个String类型值,表示调用的方法名作为“Method”对象,下面的例子中我们将用Srping类型的方法名。 public class TheButtons2 { Button thebutton; Button secondbutton; /** Creates two buttons in given container. */ public TheButtons2(AbstractComponentContainer container) { thebutton = new Button ("Do not push this button"); thebutton.addListener(Button.ClickEvent.class, this, "theButtonClick"); container.addComponent(thebutton); secondbutton = new Button ("I am a button too"); secondbutton.addListener(Button.ClickEvent.class, this, "secondButtonClick"); container.addComponent (secondbutton); } public void theButtonClick (Button.ClickEvent event) { thebutton.setCaption ("Do not push this button again"); } public void secondButtonClick (Button.ClickEvent event) { secondbutton.setCaption ("I am not a number!"); } } 添加一个监听器方法addListener()实际上只是一个包装,从一个监听适配器的方法创建一个com.vaadin.event.ListenerMethod侦听器对象,实现了java.util.EventListener接口,因此可以适应任何于任何事件源使用的接口。请注意,并不是所有的监听器类都继承了EventListener接口。 第三中方法是定义局部匿名类,这种方法往往是最简单的,因为它不需要创建新的类或接口的方法进行管理。下面的例子就是通过定义匿名类,继承了Button.ClickListener接口并实现了buttonClick()方法 public class TheButtons3 { Button thebutton; Button secondbutton; /** Creates two buttons in given container. */ public TheButtons3(AbstractComponentContainer container) { thebutton = new Button ("Do not push this button"); /* Define a listener in an anonymous class. */ thebutton.addListener(new Button.ClickListener() { /* Handle the click. */ public void buttonClick(ClickEvent event) { thebutton.setCaption ( "Do not push this button again"); } }); container.addComponent(thebutton); secondbutton = new Button ("I am a button too"); secondbutton.addListener(new Button.ClickListener() { public void buttonClick(ClickEvent event) { secondbutton.setCaption ("I am not a number!"); } }); container.addComponent (secondbutton); } } 也有其他的技术来区分两个事件源,有的使用对象属性,名称或标题来区分它们,但不赞同使用标题或其它任何可见文字作为区分条件,因为在国际化后可能会发生问题。使用其他特征文字的字符串也是很危险的,因为这样的字符串仅会在运行时被检查。 事件通常被框架发出,但有时应用程序也需要发出一些事件,比如当要更新用户界面的某些部分是有必要的。事件可以被AbstractComponent类的fireEvent(Component.Event)方法触发,然后为这个对象转发给所有特定事件类的监听器。某些组件有默认的事件类型,例如:一个Button有个嵌套的Button.ClickEvent类和相应的Button.ClickListener接口,这些事件可以被fireComponentEvent()方法触发。 4.5. 相关资源 Web应用程序工作在网页下,有着各种各样的资源,如图片或可下载文件,网页浏览器必须从服务器上获取这些资源。这些资源往往用于Embedded(图片)或Link(可下载文件)用户界面组件。各种组件,如TabSheet也可以包含图标,这也作为资源处理。 Web服务器可以在不询问应用程序而直接处理很多静态资源请求,或者由Application对象为其提供。对于动态资源,用户应用程序必须能够动态地创建它们。Vaadin为应用程序提供了可以创建各种资源的的请求接口,如文件或动态创建的资源。包括StreamResource类和分别要在11.5.1, “URI Handlers” 章节和11.5.2, “Parameter Handlers”章节介绍的URI和参数处理器。 Vaadin也为从HTTP请求检索URI和其他参数提供了底层工具。首先我们先研究如何让应用程序提供各种资源,并通过底层接口处理URI和参数以便得到资源。参考章节11.1, “Special Characteristics of AJAX Applications”。 4.5.1. 资源接口和类 Vaadin有两个资源接口:一个是通用的Resource接口,另一个是更加具体的由应用程序提供的ApplicationResource接口。 图 资源的接口和类图 ApplicationResource资源由Application类管理。当你创建一个这样的资源,你需要将应用程序对象作为构造参数,在应用程序中用addResource方法构造并注册一个资源。Application管理的资源请求可以被URI访问。RUI包含应用程序的根名称及资源的相对路径名称。相对路径名为:"APP/"+resourceid+"/"+filename,比如“APP/1/myimage.png”。resourceid是一个数字标识,确保资源中是唯一的,filename是在类构造器中指定的资源文件的名称。不过,应用程序使用的资源通常并不需要考虑它的URI,只需要给一个适当的Embedded或Link或其他的一些负责URI渲染资源的界面组件。 4.5.2. 文件资源 文件资源是存储在系统中任意位置的文件。文件资源通常分为两大类:可下载文件和嵌入式对象。 文件对象可以通过标准的java.io.File类访问,你可以以相对或绝对路径来创建文件,但相对路径的根路径依赖于Web服务器,例如在Apache Tomcat中默认当前目录是Tomcat的安装目录。 4.5.3. 资源类加载器 ClassResource允许使用Java类加载器从应用程序部署的包中获取资源,下面用一行代码展示了从应用程序包中获取图片资源,并显示在一个Embedded组件中。 mainwindow.addComponent(new Embedded ("", new ClassResource("smiley.jpg", mainwindow.getApplication()))); 4.5.4. 主题(Theme)资源 主题资源是包含在主题中的文件,比如图片就是很典型的主题资源。更多信息参考第8章 Themes 4.5.5. 字节流资源 流资源是可以创建动态资源内容的一种应用程序资源。定义一个流资源,需要你实现StreamResource.StreamSource接口的getStream方法,该方法需要返回一个可被读取的InputStream。 下面演示了如何创建一个简单的PNG图片: import java.awt.image.*; public class MyImageSource implements StreamResource.StreamSource { ByteArrayOutputStream imagebuffer = null; int reloads = 0; /* We need to implement this method that returns * the resource as a stream. */ public InputStream getStream () { /* Create an image and draw something on it. */ BufferedImage image = new BufferedImage (200, 200, BufferedImage.TYPE_INT_RGB); Graphics drawable = image.getGraphics(); drawable.setColor(Color.lightGray); drawable.fillRect(0,0,200,200); drawable.setColor(Color.yellow); drawable.fillOval(25,25,150,150); drawable.setColor(Color.blue); drawable.drawRect(0,0,199,199); drawable.setColor(Color.black); drawable.drawString("Reloads="+reloads, 75, 100); reloads++; try { /* Write the image to a buffer. */ imagebuffer = new ByteArrayOutputStream(); ImageIO.write(image, "png", imagebuffer); /* Return a stream from the buffer. */ return new ByteArrayInputStream( imagebuffer.toByteArray()); } catch (IOException e) { return null; } } } 图片内容是动态生成的,因为它每次被调用后都会重载计数器。ImageIO.write()方法将图片写到输出流,而接口返回值需要的是输入流,所以我们只好将图像内容存储到一个临时缓冲区,然后转成输入流返回。 你可以用各种方式使用资源。有些用户界面组件,如Link和Embedded把他们的参数作为资源。 下面我们展示用Embedded组件显示图片,构造StreamResource对象获取应用程序的引用,并把它自己作为资源注册到应用程序上。假设main是主窗口的引用,this是应用程序对象。 // Create an instance of our stream source. StreamResource.StreamSource imagesource = new MyImageSource (); // Create a resource that uses the stream source and give it a name. // The constructor will automatically register the resource in // the application. StreamResource imageresource = new StreamResource(imagesource, "myimage.png", this); // Create an embedded component that gets its contents // from the resource. main.addComponent(new Embedded("Image title", imageresource)); 图片将看起来像如下图所示: 图 嵌入式流资源图片的截图 资源文件名称为myimage.png,应用程序以文件名为唯一标识将资源加入,因此要确保文件名的唯一性。完整的URI比如http://localhost:8080/testbench/APP/1/myimage.png,后面的APP/1/myimage.png是URI的相对路径部分,你可以通过Application.getRelativeLocation()从URI中得到应用程序的相对路径部分。 另一个创建动态内容的方案是使用URI处理,可能还需要一些参数处理,参考章节11.5.1, “URI Handlers” 和章节 11.5.2, “Parameter Handlers”。 4.6. 关闭应用程序 用户可能已经注销或关闭了网页浏览器,那么意味着Session和相关的应用程序实例可以结束了,需要执行应用一些程序逻辑结束应用程序,否则它在Servlet会话超时后将被自动结束。 4.6.1. 关闭一个应用程序 如果用户通过用户界面退出程序,需要在Application类中调用close()方法出发一个事件处理器以便关闭会话。 按照下面的示例,我们可以放置一个Logout按钮用于用户结束会话时使用: Button closeButton = new Button("Logout"); closeButton.addListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { getMainWindow().getApplication().close(); } }); main.addComponent(closeButton); 你很快会发现,关闭应用程序简单的重新的载入了一个Application实例。你可以通过setLogoutURL方法设置窗口重定向到一个其他的URL(不重载应用程序),在你的应用程序类里这样写: setLogoutURL("/logout.html"); 4.6.2. 处理窗口的关闭 关闭主窗口(或所有应用程序级窗口)并不会关闭会话和应用程序,而应用程序是被挂起状态。你需要编程来处理窗口的关闭事件。 如果用户关闭了主窗口或其他应用程序级的窗口,窗口将会发送最后一个AJAX请求给服务器,这将会触发已关闭窗口的Window.CloseEvent事件,你可以用Window.CloseListener来处理事件,用户在这种情况下关闭浏览器,事件将发送给所有打开的窗口。 // Close the application if the main window is closed. main.addListener(new Window.CloseListener(){ @Override public void windowClose(CloseEvent e) { System.out.println("Closing the application"); getMainWindow().getApplication().close(); } }); 请注意,刷新窗口意味着关闭并重新打开它。因此,如果你有一个类似上述的关闭处理,用户可能在浏览器刷新的时候丢失。 有的时候是由于浏览器崩溃,导致没有关闭事件传送到服务器。服务器不会知道发生了什么,接着会话将被挂起直到超时,在此期间用户可重新打开浏览器,并重新进入网址,如果用户离开回来后将重新呈现主窗口,大多数情况都是希望如此的,但也有少数情况不是,此时可以建立一个安全问题。 4.7. 错误处理 4.7.1. 错误消息和提示 所有的组件都具有内置的错误提示,可以通过setComponentError()方法进行设置,如果验证失败时也可以隐藏组件。比如标题组件(Label),错误提示的位置是由组件被包含的布局来管理的,错误提示通常在标题文字的右边,鼠标悬停在错误提示上后会显示具体的提示信息。 下面的例子讲介绍如何设置组件的错误提示,这个例子实际上没有对字段做什么校验: // Create a field. final TextField textfield = new TextField("Enter code"); main.addComponent(textfield); // Let the component error be initially clear. textfield.setComponentError(null); // (actually the default) // Have a button right of the field (and align it properly). final Button button = new Button("Ok!"); main.addComponent(button); ((VerticalLayout)main.getLayout()) .setComponentAlignment(button, Alignment.BOTTOM_LEFT); // Handle button clicks button.addListener(new Button.ClickListener() { public void buttonClick(ClickEvent event) { // If the field value is bad, set its error. // (Allow only alphanumeric characters.) if (! ((String) textfield.getValue()).matches("^\\w*$")) { // Put the component in error state and // set the error message. textfield.setComponentError( new UserError("Must be letters and numbers")); } else { // Otherwise clear it. textfield.setComponentError(null); } } }); 图 错误提示被激活时 Form组件也可以处理和显示包含在其中的组件的错误消息,或者指定一个特定的区域用于显示Form中组件的错误消息。表单组件的验证介绍参考章节5.17.3, “Validating Form Input” 4.7.2. 通知 通知一般作为错误或信息的消息框显示在屏幕中央。通知框一般包含标题和可选的描述以及图标,这个通知框到达指定的时间后会自动消失或者用户直接在该区域内点击。默认已经为通知框定义了外观和行为。 通知始终是作为一种子窗口对象存在(对于整个浏览器的相对定位),Window类提供了showNotification()用于显示通知框,该方法带有标题和可选的描述及一个通知类型作为参数,该方法也接受一个Window.Notification类型的通知对象,如下所示: mainwindow.showNotification("This is the caption", "This is the description"); 图 通知 标题和描述文字默认在同一行显示,如果想让他们换行,使用XHTML标记符“
”即可。你可以使用XHTML标记符一个通知框中标题和描述。如果在得到通知时需要用户输入一些内容,请仔细阅读章节11.10.1, “Sanitizing User Input to Prevent Cross-Site Scripting”。 main.showNotification("This is a warning", "
This is the last warning", Window.Notification.TYPE_WARNING_MESSAGE); 图 通知格式化 通知类型默认定义了通知的样式和行为,如果没有指定通知类型,则默认使用“humanized”类型。下面列出定义在Window.Notification类中的所有通知类型。 TYPE_HUMANIZED_MESSAGE 一个对用户友好的,不会做太多烦扰的消息框,他不需要点击确认,很快就会消失。该消息框是个居中的灰色消息框 TYPE_WARNING_MESSAGE 警告信息是一个中等重要程度的,颜色始终可以达到提示的效果,警告框仅显示1.5秒,但用户可以点击该信息框阻止其消失,而用户可以在信息框仍然显示的情况下,继续应用程序操作。 TYPE_ERROR_MESSAGE 错误信息,是要求用户高度重视的,颜色醒目,且要求用户点击消息框后才消失,虽然右上角的关闭按钮已经很明确,但它本身并不包含点击消息的命令。它不像其他的通知消息,错误提示信息框显示的时候不允许与用户界面交互。 TYPE_TRAY_NOTIFICATION 托盘消息,该通知将显示在“系统托盘”区,即在浏览器的右下角。它不会对界面做任何的影响。它显示的时间要比TYPE_HUMANIZED_MESSAGE类型和TYPE_WARNING_MESSAGE类型要长,默认为3秒。通常托盘通知显示的时候,用户还可以与应用程序进行交互。 所有通知类型的特定功能都可以通过Window.Notification属性得以控制,你可以通过Window的showNotification()方法明确指定一个通知类型。 // Create a notification with default settings for a warning. Window.Notification notif = new Window.Notification( "Be warned!", "This message lurks in the top-left corner!", Window.Notification.TYPE_WARNING_MESSAGE); // Set the position. notif.setPosition(Window.Notification.POSITION_TOP_LEFT); // Let it stay there until the user clicks it notif.setDelayMsec(-1); // Show it in the main window. main.showNotification(notif); 通过setPosition()方法可以定位消息框的位置,用以下变量作为参数: Window.Notification.POSITION_CENTERED Window.Notification.POSITION_CENTERED_TOP Window.Notification.POSITION_CENTERED_BOTTOM Window.Notification.POSITION_TOP_LEFT Window.Notification.POSITION_TOP_RIGHT Window.Notification.POSITION_BOTTOM_LEFT Window.Notification.POSITION_BOTTOM_RIGHT 通过setDelayMSec()方法可以设置消息框显示的毫秒数。如果设置为-1则表示消息框会一直显示,直到用户点击了消息框区域。还可以设置禁止与其他用户界面交互的行为,这个在错误消息中是默认的。因为无法交互,因此错误消息框要可关闭。 4.7.3. 异常处理 Vaadin应用程序是一个事件驱动编程模型,在客户端的鼠标和键盘事件可以被服务器端的监听器处理。处理事件的过程中可能会导致应用程序或框架发生异常,但其中某些异常可能不能被正确的捕获。 例如下面的代码片段,在事件处理中我们抛出了一个异常但是并没捕获他,因此异常就落到了框架上: final Button button = new Button ("Fail Me"); button.addListener(new Button.ClickListener() { public void buttonClick(ClickEvent event) { // Throw some exception. throw new RuntimeException("You can't catch this."); } }); 任何调用链中发生类似的异常,如果任何调用链中都没有处理该异常,则异常最终会被ApplicationServlet中的终端适配器捕获,底层组件会收到客户端请求,当终端适配器捕获到所有类似的异常后,会作为一个事件通过Terminal.ErrorListener接口给Application实例的错误监听器。Application类默认不那样做,异常会在之前就抛出。 此错误的原因在于处理逻辑的逻辑处理组件在客户端和服务端之间同步状态。我们要在客户端请求时处理所有序列化参数的变化,因为客户端和服务端组件会很容易变的不同步,这将使其整个应用处于无状态。 Application类默认实现了Terminal.ErrorListener接口,简单的将错误输出道控制台,并将尝试找出关于组件的错误。如果异常发生在于监听器相关的组件上,该组件会被认为是与异常相关的组成部分。如果相关组件被发现,错误处理器将会为其设置组件错误信息,可以通过setComponentError()方法设置。 在用户界面,组件的错误会以一个小的红色的“!”标识(默认主题下),如果你将鼠标悬停在它上面,你会看到一个大的悬浮提示框。参见下图: 图 组件的异常信息显示 可以通过在你的应用程序类(一个继承Application的类)覆盖terminalError()方法很容易的改变终端处理异常的逻辑,或者通过设置setErrorHandler方法定义一个自定义的错误监听器。你可以安全的放弃默认的处理方式,或扩展你自己的自定义处理方式,再或者记录系统日志。在下面的例子中,在主窗口中将异常信息作为通知汇报给用户。 @Override public void terminalError(Terminal.ErrorEvent event) { // Call the default implementation. super.terminalError(event); // Some custom behaviour. if (getMainWindow() != null) { getMainWindow().showNotification( "An unchecked exception occured!", event.getThrowable().toString(), Notification.TYPE_ERROR_MESSAGE); } } 其他异常通常用Java Servlet的方式来处理,未捕获的异常最终能够会被应用服务器捕获并处理。 4.8. 设置应用程序环境 虽然基于服务器的框架、类库和一些为Java创造的体系结构越来越多,使得程序员的编程生活过的很轻松,但是软件的部署似乎越来越难。比如,EJB设法使持久和网络对象创建的更加容易且自动化,但是需要配置大量的部署描述符。由于Vaadin依赖于Java Servlet容器,它必须遵守规范,但也会避免增加额外的复杂性。 所有打包成WAR文件的Vaadin应用程序都作为一个Web应用部署。对于如何打包一个web应用程序的教程明细,请参考任何关于Java Servet的书籍,Sun公司在已经在网上有了一个很好的参考http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/WCC3.html [http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/WCC3.html]。 4.8.1. 用Eclipse创建WAR部署包 部署一个用用程序到Web服务器,你需要创建一个WAR包,这里用Eclipse做个说明, 打开Eclipse,在项目上鼠标右键,从选择“Export…”,弹出的树型对话框中选择Web->War file,在WAR Export导出选项设置中设置名称和导出路径,点击Finish完成导出。 4.8.2. WEB应用程序上下文 下面这些文件是Web应用程序所必需的文件。 Web应用程序组织结构 l WEB-INF/web.xml 这个是Web应用程序标准的描述文件,它定义了Web应用程序是怎样被组织的。你可以参考任何对于该文件内容有关的Java书籍,也可以参考示例4.1, “web.xml”。 l WEB-INF/lib/vaadin-6.2.0.jar 这个文件是Vaadin的类库包,包含在项目的lib目录下。 l 应用程序的类 你必须把class类放到WEB-INF/classes目录下,或打成一个JAR包放在WEB-INF/lib目录下。 l 主题文件(可选) 如果你的应用中包含特定的主题(视觉感受),你必须将它放到WEB-INF/lib/themes/themename目录中。 4.8.3. 部署描述符文件web.xml 描述符文件是一个以web.xml命名的XML文件,存在WEB应用程序的WEB-INF目录下,它是一个如何部署应用的J2EE标准组件,其文档结构参看下列示例,你仅需要将一个实现了Servelt的com.vaadin.terminal.gwt.server.ApplicationServlet包装类部署到应用程序中。 Servlet类通过一个类的名称定义指定的应用参数,用Java Servelt标准方式连接到一个URL 例 web.xml myservlet com.vaadin.terminal.gwt.server.ApplicationServlet application MyApplicationClass myservlet /* 上例中用myservlet为Servlet的名称,Servlet类com.vaadin.terminal. gwt.server.ApplicationServlet是Vaadin框架所需的,这对于所有Vaadin应用程序都一样。 上面的url-pattern定义为“/*”,意味着将匹配应用程序中所有的URL。我们在定义项目的上下文名称为myproject,所以应用的访问URL为http://localhost:8080/myproject/。如果该项目是拥有或多个应用程序或Servlet,他们就必须赋予不同的名称来区分它们。例如:url-pattern为/myapp/*将匹配http://localhost:8080/myproject/myapp/,注斜线和星号必须配置在末尾。 还要注意如果url-pattern是 “/*”以外的值,还需要让Servlet映射到/VAADIN/*,例如: ... myservlet /myurl/* myservlet /VAADIN/* 如果你的部件集和主题资源静态化到WebContent/VAADIN/目录下,你可以不必配置 /VAADIN/*映射。映射仅服务于从Vaadin的JAR文件处理的动态资源。建议采用静态方式,尤其是生产环境,因为那样速度会快很多。 对于如何部署应用的完整的例子,参考Vaadin安装包里的示例,尤其是WebContent/WEB-INF目录。 4.8.4. 部署描述符参数 部署描述符有很多参数和选项用于控制Servlet的执行,关于部署描述符的完整文档参考Java Servlet规范:http://java.sun.com/products/servlet/。 在开发过程中,Vaadin应用默认运行在调试模式,这将启动各种调试功能,作为生产环境使用时,你应该修改web.xml文件调整这个参数其实关闭调试模式: productionMode true Vaadin production mode 调试模式和生产模式参数将在章节11.4 “Debug and Production Mode”.予以讨论。 对于Session的过期时间的调整也是很有必要的,不同的servlet容器有不同的默认超时时间,比如Tomcat默认超时时间为30分钟,你可以按下面方法修改web.xml以便调整超时时间: 30 当Session超时后,Application类的close()方法将被调用,因此你需要在该方法中处理超时后的情况。 5. 用户界面组件 5.1. 概述 略 5.2. 接口和抽象 Vaadin用户界面组件构建在大量的接口和抽象类的基础上,抽象类中定义和实现了所有组件的公共特性以及如何在服务器与客户端之间组件状态序列化的基本逻辑。章节 6 Managing Layout会介绍图层和组件的抽象描述。在章节 9 Binding Components to Data描述了与界面组件相关的Vaadin数据模型。 图 组件接口与抽象 所有组件也实现了Paintable接口,用于向客户端序列化(“画”)组件,以及反向操作的VariableOwner接口,这是需要将客户端组件反序列化组件状态或用户接口。 除了在Vaadin框架中定义的接口,所有组件可以被序列化,因为均实现了java.io.Serializable接口。序列化是需要很多集群或云计算解决方案。 5.2.1. Component接口 一般Component接口总是和AbstractComponent类配对,并实现里面所有的接口方法。 组件树管理 组件被放置在用户展现层,布局由布局管理器负责,或很多组件实现了ComponentContainer接口,这种容器是包含父组件的。 组件的getParent()方法可以返回父组件,虽然有一个setParent(),但很少使用,一般都是通过ComponentContainer接口的addComponent()方法来自动设置父组件。一个组件并不知道它的父组件什么时候被创建,因此你不能在构造器中用getParent()得到父组件。此外,你也不能在得到父附件之前通过getApplication()得到应用程序对象的引用,例如下面的例子是无效的: public class AttachExample extends CustomComponent { public AttachExample() { // ERROR: We can't access the application object yet. ClassResource r = new ClassResource("smiley.jpg", getApplication()); Embedded image = new Embedded("Image:", r); setCompositionRoot(image); } } 组件被添加同时应用程序触发器将会为组件调用attach()方法,相应的,如果移除一个组件触发器会调用detach()方法。如果一个已添加组件的父组件已经连接到了应用程序,attach()方法被调用时,setParent()方法也将立即被调用。 public class AttachExample extends CustomComponent { public AttachExample() { } @Override public void attach() { super.attach(); // Must call. // Now we know who ultimately owns us. ClassResource r = new ClassResource("smiley.jpg", getApplication()); Embedded image = new Embedded("Image:", r); setCompositionRoot(image); } } 上面逻辑代码继承了AbstractComponent,该类的详细内容参考章节5.2.2 “AbstractComponent”. 5.2.2. AbstractComponent AbstractComponent类是所有用户界面组件的基类,它仅实现了Component接口及接口中的所有方法。 AbstractComponent类中只有一个抽象方法:getTag(),返回组件类的一个特定的序列化标识,当建立一个新的组件时(仅在这个时候)需要实现这个方法。AbstractComponent管理大多数在客户端和服务端之间组件的序列化状态。组件的创建和序列化参考章节10, Developing Custom Components,服务端序列化API在附录A, User Interface Definition Language (UIDL)进行说明 5.2.3. 字段组件(Field and AbstractField) 字段组件拥有一个值的属性,可以在用户界面对该值进行修改,下图介绍了类的继承关系、重要的接口和基类。 图 Field组件 字段组件是建立在框架中的Field接口和AbstractField基类之上的。字段组件含有强健的Vaadin数据模型。组件的值由Property处理,选择字段通过Container接口对可选项进行管理。 下面章节将对接口和基类的细分类做详细介绍。 Field接口 Field接口继承了Component接口以及一个拥有字段值的Property接口。AbstractField类仅实现了Field接口。参看下面截图: 图 字段接口继承类图 你可以通过Property接口中定义的setValue()方法设置字段值, getValue()方法得到字段值,值的类型取决于组件。 Field接口定义了很多属性,你可以通过getter和setter方法来访问和操作它们。 description 所有字段都有一个描述,但这个属性仅存在于Field组件,是被AbstractField,实现的。Field并没有对其做实现,只能通过AbstractField类。 required 字段可以标记是否必填,一个被标记必填的字段一般在字段前会显示一个“*”,如果验证为空的字段,会显示组件的错误提示信息,该信息通过requiredError属性设置(见下文)。 requiredError 定义一个当字段必填验证失败时显示的文本文字。错信息就是该属性进行定义的,通常显示在一个提示框中,Form组件可以指定错误信息显示的区域。 Field值变化处理 Field继承Property.ValueChangeListener类,当字段值发生改变时会触发该监听器。Property.Editor允许编辑值。 当字段值发生改变时,字段的Property.ValueChangeEvent将被触发,你不必实现AbstractField类中的valueChange()方法,它已经在中AbstractField被实现,但是你需要实现一个监听器方法。 AbstractField基类 AbstractField类是所有字段组件的基类,除了从AbstractComponent类继承过来的方法外,也实现了来自Property,Buffered,Validatable和Component.Focusable接口的特性功能方法。 5.3. 公共组件特性 基类组件提供了大量的功能,让我来看看一些最常用的功能,具体特性请参考Vaadin的JavaDoc。 接口中包含了很多可以直接用geeter和setter方法获取和操作的属性。 5.3.1. 标题 标题是伴随用户界面组件的解释性文字,通常显示在组件上方或组件里面。由于标题文字自动引用,因此它不能通过XHTML来重绘。 通常标题都是作为组件构造器的第一个参数,或者在实例化后通过setCaption()方法设置。 // New text field with caption "Name" TextField name = new TextField("Name"); layout.addComponent(name); 组件的标题默认是被布局组件管理和显示或由组件的容器所定。例如VerticalLayout中的组件标题显示组件的在组件垂直对齐的上方,FormLayout中的组件标题文字显示在组件的左侧,CustomComponent根组件不负责管理标题显示位置,因此即使有根组件也不会被渲染。 图 VerticalLayout和FormLayout管理的组件标题 有些组件,如Button和Panel,是由自己管理标题和现实。 图标(参考5.3.4, “Icon”)与标题密切相关,通常水平显示在标题的之前或之后,这主要取决于组件和布局。 实现标签的另一个方法是用其他组件来作为标签,通常用Label、TextField或 Panel。例如,允许用XHTML标记一个快捷方式或绑定标题一个数据源。Panel提供了简单的方法添加一个标题和一个围绕组件的边框。 通过设置setCaption()方法会导致组件被即时更新。复写setCaption()方法需要先调用父类的实现(super. setCaption())。 CSS样式规范 标题的样式会默认采用<组件>-caption格式为CSS样式的名称。例如,TextField的样式名为v-textfield-caption,其中包含了一个可能与布局有关的标识符号。 5.3.2. 描述和工具提示 所有继承AbstractComponent的组件都有一个描述,通常当鼠标悬停在组件片刻后会显示以一个工具提示。可以通过setDescription()方法设置描述内容和getDescription()方法查看描述内容。 Button button = new Button("A Button"); button.setDescription("This is the tooltip"); 图 以一个工具提示方式显示描述 描述大多数都是通过工具提示展现,Form在组件的顶部区域显示,具体参考章节5.17.1, “Form as a User Interface Component”。 当组件通过setComponentError()设置了组件错误信息,错误提示也通常用工具提示的方式显示,以下将做说明(Form将显示在底部区域),组件在错误状态时也会显示错误提示。参考章节4.7.1, “Error Indicator and message”。 描述其实并不是真的纯文本,它可以使用一段XHTML标签来格式化,丰富的内容可以包含图片和任何HTML元素。 button.setDescription( "

"+ "A richtext tooltip

"+ "
    "+ "
  • Use rich formatting with XHTML
  • "+ "
  • Include images from themes
  • "+ "
  • etc.
  • "+ "
"); 结果显示如下图: 图 丰富的文本工具提示 描述的setter和getter方法定义在所有Field接口,并非所有Component接口 5.3.3. 是否启用 “启用”属性控制用户是否可以使用该组件,一个禁用的组件是可见的,但是是灰色且不可使用的。 组件默认都是启用状态的,你可以使用setEnabled(false)方法将其置为禁用。 Button enabled = new Button("Enabled"); enabled.setEnabled(true); // The default layout.addComponent(enabled); Button disabled = new Button("Disabled"); disabled.setEnabled(false); layout.addComponent(disabled); 关于启用和禁用效果见下图 图 一个启用和禁用的按钮 禁用的组件自动被设置成只读状态,没有这样的组件客户端的交互被发送到服务器,并作为重要的安全功能,服务器端组件不接收来自客户端的状态更新。 CSS样式规范 禁用组件使用v-disabled为组件的特定CSS样式名。为配合两个组件的样式,你必须加入一个点,如下面的例子做样式类名称。 .v-textfield.v-disabled { border: dotted; } 这将使文本输入框周围的边框为点线框 TextField disabled = new TextField("Disabled"); disabled.setValue("Read-only value"); disabled.setEnabled(false); layout.addComponent(disabled); 结果如下图: 5.3.4. 图标 图标是一个附带说明的图形用户界面的标签组件,通常显示在上方,左边或组件内部。图标与标题密切相关(参考章节5.3.1 “Caption”),通常显示在标题组件的前或后面,这主要取决于组件和布局。 可以通过setIcon()方法设置图标组件,图片以一个资源形势提供,一般常用ThemeResource。 // Component with an icon from a custom theme TextField name = new TextField("Name"); name.setIcon(new ThemeResource("icons/user.png")); layout.addComponent(name); // Component with an icon from another theme ('runo') Button ok = new Button("OK"); ok.setIcon(new ThemeResource("../runo/icons/16/ok.png")); layout.addComponent(ok); 图标组件默认是被布局组件管理和显示或由组件的容器所定。例如VerticalLayout中的组件标题显示组件的在组件垂直对齐的上方,FormLayout中的组件标题文字显示在组件的左侧,CustomComponent根组件不负责管理标题显示位置,因此即使有根组件也不会被渲染。 图 显示主题资源中的一个图标 有些组件,如Button和Panel,是由自己管理标题和现实。 CSS样式规范 图标组件使用v-icon为组件的特定CSS样式名。容器布局可能会附上一个图标和一个内部元素相关的标题标题,如v-caption。 5.3.5. 本地化 在组件中,用一个区域属性定义了国家和语言。你可以通过本地信息获取对应的本地化资源进行国际化处理。如DateField等一些组件就是进行了本地化。你可以用setLocale()方法设置本地化信息。 // Component for which the locale is meaningful InlineDateField date = new InlineDateField("Datum"); // German language specified with ISO 639-1 language // code and ISO 3166-1 alpha-2 country code. date.setLocale(new Locale("de", "DE")); date.setResolution(DateField.RESOLUTION_DAY); layout.addComponent(date); 显示效果如下图所示: 图 为日期选择组件设置国际化信息 你可以通过getLocale()方法得到本地信息组件对象。如果没有为组件设置本地信息组件,将使用父组件的语言环境,如果父组件没有设置语言环境,将使用应用的语言环境,如果也没有设置,则默认使用系统的语言环境,如从Locale.getDefault()获得。 因为要求组件必须附加到应用程序,因此在序列化时调用getLocale()方法变得很尴尬,你不能在构造器中使用它,你只能通过在attach()中得到语言环境设置,如下面例子所示: Button cancel = new Button() { @Override public void attach() { ResourceBundle bundle = ResourceBundle.getBundle( MyAppCaptions.class.getName(), getLocale()); setCaption(bundle.getString("CancelKey")); } }; layout.addComponent(cancel); 一个更好的做法是从应用程序全局参数中得到语言环境对象,当组件被创建时用它来获取本地化资源。 // Captions are stored in MyAppCaptions resource bundle // and the application object is known in this context. ResourceBundle bundle = ResourceBundle.getBundle(MyAppCaptions.class.getName(), getApplication().getLocale()); // Get a localized resource from the bundle Button cancel = new Button(bundle.getString("CancelKey")); layout.addComponent(cancel); 选择一个语言环境 很多应用中常见的一项任务就是选择语言环境,下面就是一个用Select组件演示的例子: // The locale in which we want to have the language // selection list Locale displayLocale = Locale.ENGLISH; // All known locales final Locale[] locales = Locale.getAvailableLocales(); // Allow selecting a language. We are in a constructor of a // CustomComponent, so preselecting the current // language of the application can not be done before // this (and the selection) component are attached to // the application. final Select select = new Select("Select a language") { @Override public void attach() { setValue(getLocale()); } }; for (int i=0; i元素。 Embedded.TYPE_IMAGE 嵌入一个图片为HTML的元素 Embedded.TYPE_BROWSER 嵌入一个浏览器框剪为HTML的" + "
" + "" + "" + "" + "" + " " + "
" + usernameCaption + "
" + passwordCaption + "
" + "
" + "
" + "" + "" + submitCaption + "" + "
"; 然后合并字符串返回给页面一个字节数组。 return (x + "" + h + b + "").getBytes(); } 最后我们可以用下面的方式使用自定义表单: MyLoginForm loginForm = new MyLoginForm("Name of the User", "A passing word", "Login Me Now"); 以下为效果图: 图 定制登录表单 用CSS样式定义样式 .v-customcomponent {} .v-customcomponent .v-embedded {} .v-app-loginpage {} .v-app-loginpage .v-textfield {} .v-app-loginpage .v-button {} LoginForm组件是一个纯粹的服务器端组件,它扩展自CustomComponent组件,因此他具有一个v-customcomponent样式,如果你想为组件调整样式,你需要为其定制一个自定义的样式名以区分开CustomComponent组件的样式。 组件包含在一个拥有v-embedded样式的ifarme里,其他的样式定义在通过getLoginHTML()方法返回的静态HTML里,TextField和Button使用输入框和按钮默认的样式,分别是v-textfield和v-button。根组件又有与Vaadin应用程序一样的v-app样式,和一个附加的v-app-loginpage样式。 ... + "
" ... + " 5.21. 用CustomComponent自定义组件 易于创建新的用户界面组件是Vaadin的重要功能之一。您只需结合现有的内置组件创建复合组件。在很多应用程序中,复合组件构成了大部分的用户界面。 要创建一个复合组件,需要继承CustomComponent,在构造器中调用setCompositionRoot()方法设置复合根组件。根组件通常是一个布局组件,它可以包含多个组件。 class MyComposite extends CustomComponent { public MyComposite(String message) { // A layout structure used for composition Panel panel = new Panel("My Custom Component"); panel.setContent(new VerticalLayout()); // Compose from multiple components Label label = new Label(message); label.setSizeUndefined(); // Shrink panel.addComponent(label); panel.addComponent(new Button("Ok")); // Set the size as undefined at all levels panel.getContent().setSizeUndefined(); panel.setSizeUndefined(); setSizeUndefined(); // The composition root MUST be set setCompositionRoot(panel); } } 应注意尺寸的大小应足以包含所有组件。你必须将各级组件的大小设置为未定义(panel.setSizeUndefined()),复合组件的尺寸和根组件是分开的。 你可以使用自定义组件: MyComposite mycomposite = new MyComposite("Hello"); 图 自定义复合组件 你也可以继承其他组件,以达到类似布局的效果。甚至,你可以通过继承GWT自定义组件或扩展GWT内置客户端组件创建一个全新的底层组件。GWT自定义组件的开发将在章节10, Developing Custom Components.介绍。 6. 布局管理 7. 用Eclipse设计用户界面 8. 主题(皮肤) 9. 组件与数据的绑定 9.1. 概述 Vaadin数据模型是类库的核心概念之一。(207) 9.2. 属性 9.3. 在Item中处理属性 9.4. 收集容器中的Item 10. 自定义组件开发 11. Web应用程序高级主题 12. 用户界面定义语言(UIDL)

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享文档获得金币 ] 9 人已下载

下载文档