跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

JSF 2 简介: HTML5 复合组件,第 1 部分

开始使用 JSF 2 实现 HTML5 组件库

David Geary, 总裁, Clarity Training, Inc.
David Geary 的照片
David Geary 是一名作家、演讲家和顾问,也是 Clarity Training, Inc. 的总裁,他指导开发人员使用 JSF 和 Google Web Toolkit (GWT) 实现 Web 应用程序。他是 JSTL 1.0 和 JSF 1.0/2.0 专家组的成员,与人合作编写了 Sun 的 Web Developer 认证考试的内容,并且为多个开源项目作出贡献,包括 Apache Struts 和 Apache Shale。David 的 Graphic Java Swing 一直是关于 Java 的畅销书籍,而 Core JSF(与 Cay Horstman 合著)是关于 JSF 的畅销书。他还是 GWT Solutions 一书的作者。David 经常在各大会议和用户组发表演讲。他从 2003 年开始就一直是 NFJS tour 的定期演讲人,并且在 Java University 教授课程,三次当选为 JavaOne 之星。

简介: HTML5 为基于浏览器的应用程序提供可与桌面软件相媲美的丰富功能。在这一期的 JSF 2 fu 中,我们将使用 JavaServer Faces (JSF) 2 实现 HTML5 复合组件,您将明白如何最高效地利用 Java™ 和 HTML5。

查看本系列更多内容

发布日期: 2010 年 12 月 27 日
级别: 中级 原创语言: 英文
访问情况 : 8812 次浏览
评论: 


HTML5 显而易见是软件开发的下一大热门。起初以 Web 应用程序而为人所知的 HTML5 最终将桌面应用程序的强大功能 — 通过拖放、画布、视频和音频等设施完成 — 引入了浏览器。HTML5 集各种技术(特别是规范)于一体,形成了一个囊括 HTML、JavaScript 和层叠样式表(CSS)在内的功能强大的 API。以下是一些 HTML5 要点:

关于本系列

JSF 2 fu 系列是继 David Geary 同名的 三篇文章简介 推出的,像一个功夫大师一样帮助您发展和磨练您的 JSF 2 框架技能。本系列深入专研框架及其周围的生态系统,并从外部视角展示一些 Java EE 技术,比如 Contexts and Dependency Injection,如何与 JSF 集成。

  • 画布
  • 拖放
  • 地理位置*
  • 内联编辑
  • Web workers*
  • Web 存储*
  • 消息传递
  • 离线应用
  • 视频和音频*
  • Web 套接字*

要注意地理位置和离线应用这样的前瞻性功能。(我用星号标记的功能从技术上讲不是 HTML5 规范的一部分呢,不过一般通俗地使用术语 HTML5 来包含我列出的所有功能。参见 参考资料 了解更多信息。)

从某些方面看,HTML5 就是下一个 Java。在 20 世纪 19 年代后期,Java 语言大受欢迎,主要是因为其编写一次,到处运行 的功能使开发人员免于具体选择(或移植到)Windows®、Mac 或 Linux®。HTML5 允许您编写一次,在任何(现代)浏览器中运行,因此您无需在 iOS、Android 和 Chrome 之间做出选择。

编写一次,随处调试?

Java 技术允许您为多个操作系统编写一个应用程序,但事实上并非完全如此。HTML5 也是这样。HTML5 不提供本机操作系统提供的一些设施,比如与加速计进行交互。(尽管有工具包 — 比如 PhoneGap [参见 参考资料] — 可以弥补空缺。这些缺点总是使一些开发人员避开 HTML5 而垂青本机应用程序。但是对于大部分应用程序,HTML5 提供更好的投资回报。

Java 技术

HTML5 可能就是下一个 Java,但是不会取代它。Java 技术为服务器端编程提供丰富的生态系统。而最初基于 HTML 的 JSF 允许您便捷地使用 HTML5,如同至今为止通过 JSF 使用 HTML4 一样。您将获得所有强大的 JSF 功能,比如,除了 HTML5 之外,还有 facelets 模板、复合组件和内置 Ajax。

在本文中,您将学习如何使用 JSF2 创建 HTML5 复合组件。在下一篇 JSF 2 fu 文章中,我将向您展示如何创建 HTML5 组件的一个库。


开始使用 HTML5

使用 HTML5 其实更多地是涉及到 JavaScript 而非 HTML。也就是说,您需要有一个很好的 JavaScript 调试器。我建议使用 Google Chrome 的内置开发工具中自带的调试器(参见 参考资料),如图 1 所示:


图 1. 使用 Chrome 开发工具调试 JavaScript
Google Chrome 的内置开发工具自带的 JavaScript 调试器屏幕截图

图 1 所示的 Chrome 调试器中,在 canvas 组件下面有一个包含 JavaScript 代码的面板。

现在,有了一个较好的 JavaScript 调试器,您只需要一个可支持 HTML5 的浏览器。较流行的浏览器的大部分最新版本都可以很好地支持 HTML5。(微软似乎在其即将发布的 Internet Explorer 9 中具有良好的 HTML5 支持。)


使用 HTML5 canvas

HTML5 canvas 是一种成熟的 2D 绘图界面,足以完成 Plants vs. ZombiesQuake II 这样的游戏。我对 HTML5 canvas 的使用,如图 2 所示,可能没有那么深入,但是作为指导已经足够了:


图 2. 一个简单的 HTML5 canvas 示例
浏览器屏幕截图,显示通过使用 HTML5 canvas 的绘画应用程序创建的蓝色卡通人物

我向 HTML5 canvas 添加了一些 JavaScript 来实现 图 2 所示的简单绘画应用程序。在移动鼠标时,画布左上角的读出器中显示鼠标坐标。在画布中拖动鼠标时,使用蓝色画笔绘图。

图 2 中所示的应用程序是一个 JSF 应用程序。图 3 显示其目录结构:


图 3. canvas 示例的目录结构
canvas 示例的目录结构屏幕截图

应用程序的 facelet 在 web/WEB-INF/index.xhtml 中定义,且应用程序的 JavaScript 位于 web/resources/application/paintingCanvas.js 中。清单 1 显示 index.xhtml:


清单 1. 使用 <canvas> tag (WEB-INF/index.xhtml)
				
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html">

   <h:head>
      <title>#{msgs.windowTitle}</title>
   </h:head>
   
   <h:body style="background: #fefeef">
      <h:outputScript library="application" name="paintingCanvas.js" 
                       target="head" />
      
      <h3>#{msgs.heading}</h3>
   
   
      <canvas width="400px" height="400px" id="paintingCanvas">

         Canvas not supported. 
         
      </canvas>     
   </h:body>  
   
</html> 

清单 1 中的 index.xhtml 文件是一种 HTML5 多语言文档(参见 参考资料),因为它有一个 HTML5 文档类型/名称空间和格式标准的 XHTML 语法 — 这正是我酝酿 facelets 和 HTML5 所必需的。

我使用 <h:outputScript> 标记导入相应的 JavaScript。最后,我开始使用 HTML5 canvas 元素。如果浏览器不明白 <canvas> 标记,它会显示一条 Canvas not supported 消息。<canvas> 标记不会高亮显示;所有有趣的代码在相应的 JavaScript 中,如清单 2 所示:


清单 2. 绘图画布 JavaScript (resources/application/paintingCanvas.js)
				
window.addEventListener("load", function() {
   var canvas, context, painting;
   
   function init() {
      canvas = document.getElementById("paintingCanvas");
      if (canvas == null) return;
      
      context = canvas.getContext("2d");
      if (context == null) return;
   
      painting = false;
      
      context.strokeStyle = "#00f";
      context.lineWidth = 3;
      context.font = "15px Helvetica";
   }

   init();
   
   canvas.addEventListener("mousedown", function(ev) {
      painting = true;
      context.beginPath();
      context.moveTo(ev.offsetX, ev.offsetY);
   }, false);
   
   canvas.addEventListener("mousemove", function(ev) {
      updateReadout(ev.offsetX, ev.offsetY);

      if (painting) {
        paint(ev.offsetX, ev.offsetY);
      }
      function updateReadout(x, y) {
         context.clearRect(0, 0, 100, 20);
         context.fillText("x: " + x + ",  y: " + y, 5, 15);
      }
      function paint(x, y) {
         context.lineTo(ev.offsetX, ev.offsetY);
         context.stroke();
      }
         
   }, false);
   
   canvas.addEventListener("mouseup", function() {
      painting = false;
      context.closePath();
   }, false);
   
   
 }, false);

运行样例代码

本系列的代码基于运行于企业容器(比如 GlassFish 或 Resin)内的 JSF 2。参阅本系列第一期 “JSF 2 fu: Ajax 组件”,这是个分步教程,讲解如何使用 GlassFish 安装和运行本系列的代码。参见 下载 部分,获取本文的样例代码。

清单 2 使用鼠标光标读出器实现简单的绘画,如 图 2 所示。当页面加载时,我使用 document.getElementById() 获取对画布的引用。从画布中,我获取对画布上下文的引用。我在后续的事件处理程序中使用该内容,我使用 JavaScript 闭包或 Java 开发人员所指的匿名内部类实现它。

如果您使用过 Abstract Window Toolkit (AWT),就会了解画布内容是对 AWT 的图形内容的模仿。毕竟,绘制二维的形状、图像和文本有这么多方式。在 清单 2 中,我使用蓝色的描边风格 初始化上下文,并设置了线宽和字体。之后所需的操作就只是移动、描边、重复、鼠标上下拖动。

学习了 HTML5 canvas 基础知识之后,我将向您展示如何创建一个 JSF 2 HTML5 复合组件。


一个 JSF 2 HTML5 canvas 组件

接下来,我要实现一个使用 HTML5 canvas 的 JSF 2 复合组件。我需要复合组件实现以下要求:

  • 拥有可配置的(通过标记属性)宽度、高度、画笔颜色、线宽和 CSS 样式
  • 将组件标记的正体作为 canvas not supported 消息
  • 自动包括 canvas 的 JavaScript
  • 在一个页面中支持多个 canvas 组件

使用 canvas 复合组件的应用程序如图 4 所示:


图 4. 动态 canvas 复合组件
动态 canvas 复合组件屏幕截图

图 4 中所示的应用程序有三个 canvas 组件,每个组件的配置都有所不同。从左边开始,前两个是绘图画布,类似于 图 2 中显示的那一个。最右边的画布仅绘制一个笑脸。

清单 3 显示 图 4 中显示的页面的标记:


清单 3. 使用 canvas 复合组件(WEB-INF/index.xhtml)
				
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:h5="http://java.sun.com/jsf/composite/html5">

   <h:head>
      <meta charset="UTF-8" />
      <title>#{msgs.windowTitle}</title>
   </h:head>
   
   <h:body style="background: #fefeef">
      
      <h3>#{msgs.heading}</h3>
   
      <h5:canvas id="paintingCanvas" width="300px" height="300px" 
                 penColor="#7F7" lineWidth="7">
              
         #{msgs.canvasUnsupported}

      </h5:canvas>     
      
      <h5:canvas id="secondPaintingCanvas" width="300px" height="300px"
                 style="border: thick solid red">

         #{msgs.canvasUnsupported}

      </h5:canvas>  
      
      <h5:canvas id="smileyCanvas" library="application" script="smiley.js"
                 width="300px" height="300px"
                 style="border: thin solid red">

         #{msgs.canvasUnsupported}

      </h5:canvas>
   </h:body>  
   
</html> 

清单 3 中,canvas 组件导入适当的 JavaScript — 不同于 清单 1,其中我手动使用 HTML5,且必须显式地导入相关的 JavaScript。页面创建者可以使用 canvas 组件的可选 libraryscript 属性来制定一个画布的 JavaScript,或者他们可以依赖于默认的 JavaScript。在 清单 3 中,我为笑脸画布使用 script 属性。我为示例中最左边的两个画布使用默认的 JavaScript(resources/html5/canvasDefault.js,实现绘图画布)。

清单 4 显示 canvas 复合组件的实现:


清单 4. canvas 复合组件(resources/html5/canvas.xhtml)
				
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

  <composite:interface>
    <composite:attribute name="id"/>  
    <composite:attribute name="width"/>
    <composite:attribute name="height"/>   
    <composite:attribute name="library"  default="html5"/>
    <composite:attribute name="script"   default="canvasDefault.js"/>
    <composite:attribute name="style"    default="border: thin solid blue"/>
    <composite:attribute name="penColor" default="#7777FF"/>
    <composite:attribute name="lineWidth" default="2"/>
  </composite:interface>
  
  <composite:implementation>
      <canvas id="#{cc.id}"
           width="#{cc.attrs.width}" 
          height="#{cc.attrs.height}"
           style="#{cc.attrs.style}">
                                
      <composite:insertChildren/>
           
     </canvas>
    
    <h:outputScript library="#{cc.attrs.library}" 
                       name="#{cc.attrs.script}"/>
                       
    <script>
      #{cc.attrs.script}.init('#{cc.id}', 
                              '#{cc.attrs.penColor}', 
                              '#{cc.attrs.lineWidth}')
    </script>                                                 
  </composite:implementation>
</html>

清单 4 中,我为 canvas 复合组件声明了 8 个属性,其中 5 个具有默认值。组件的实现包含一个 HTML5 canvas,带有从组件的相关属性中配置的 ID、宽度、高度和样式。这满足了我对 canvas 组件的第一个要求(可配置属性)。

canvas 复合组件将其子元素 — 即将 <h5:canvas> 标记正体中的任何内容 — 插入到 HTML5 canvas 标记(<canvas>)中。这表示,如果浏览器不支持 HTML5 canvas,标记正体中的文本会显示出来。这满足了第二个要求(将组件标记的正体作为 canvas not supported 消息)。

canvas 组件包含一个 <h:outputScript> 标记,该标记导入 canvas 的 JavaScript,这由 canvas 组件的 libraryscript 属性指定。注意,libraryscript 属性的默认设置分别为 html5canvasDefault.js。这满足了第三个要求(自动导入 canvas 的 JavaScript)。

最后,canvas 组件调用一个名为 init() 的 JavaScript 方法,传递 canvas 的 ID、画笔颜色和线宽。init() 方法获取并初始化 canvas 的上下文。注意我说的是方法,而非函数,因为 init() 方法属于一个对象。该对象名源自于 canvas 组件的 script 属性。例如,对于 清单 3 中的笑脸画布,我指定 smiley.js 的一个 script 值,因此笑脸画布组件会调用 smiley.js.init()js 对象的 init() 方法,包含在一个名为 smiley 的对象中。如果您不显式地指定 script 值,其默认设置为 canvasDefault.js,因此 JavaScript 方法会是 canvasDefault.js.init()。调用这些方法,而非全局函数,这使我满足了第四个要求:在一个页面中支持多个画布。

canvas 组件的默认 JavaScript 如清单 5 所示:


清单 5. 默认的 canvas JavaScript (resources/html5/canvasDefault.js)
				
if (!canvasDefault) var canvasDefault = {}

if (!canvasDefault.js) {
  canvasDefault.js = {
    init : function(canvasId, penColor, lineWidth) {
      var canvas, context, painting;

      canvas = document.getElementById(canvasId);
      if (canvas == null) {
        alert("Canvas " + canvasId + " not found")
      }

      context = canvas.getContext("2d")
      if (context == null)
        return;

      painting = false;

      context.strokeStyle = penColor
      context.lineWidth = lineWidth
      context.font = "15px Helvetica"

      canvas.addEventListener("mousedown", function(ev) {
        painting = true
        context.beginPath()
        context.moveTo(ev.offsetX, ev.offsetY)
      }, false)

      canvas.addEventListener("mousemove", function(ev) {
        updateReadout(ev.offsetX, ev.offsetY)

        if (painting) {
          paint(ev.offsetX, ev.offsetY)
        }
        function updateReadout(x, y) {
          context.clearRect(0, 0, 100, 20)
          context.fillText("x: " + x + ",  y: " + y, 5, 15)
        }
        function paint(x, y) {
          context.lineTo(ev.offsetX, ev.offsetY)
          context.stroke()
        }

      }, false)

      canvas.addEventListener("mouseup", function() {
        painting = false
        context.closePath()
      }, false)
    }
  }
}

清单 5 中,我创建一个名为 canvasDefault 的对象,包含一个名为 js 的对象,该对象包含一个 init() 方法。我这样做是为了为 init() 方法创建名称空间,这样它就不会被另一个全局 init() 函数所覆盖。这样一来,我就可以在一个页面中含有多个画布,所有画布都有其自己的 init() 函数的实现。

清单 6 显示笑脸画布的 JavaScript:


清单 6. 笑脸画布的 JavaScript (resources/application/smiley.js)
				
if (!smiley) var smiley = {}

if (!smiley.js) {
  smiley.js = {
    init : function(canvasId, penColor, lineWidth) {
      var canvas, context

      canvas = document.getElementById(canvasId);
      if (canvas == null) {
        alert("Canvas " + canvasId + " not found")
      }

      context = canvas.getContext("2d");
      if (context == null)
        return

      // smiley face code originally downloaded
      // from thinkvitamin.com
        
      // Create the face

      context.strokeStyle = "#000000";
      context.fillStyle = "#AAAAFF";
      context.beginPath();
      context.arc(100,100,50,0,Math.PI*2,true);
      context.closePath();
      context.stroke();
      context.fill();
      
      // eyes              
      context.strokeStyle = "#000000";
      context.fillStyle = "#FFFFFF";
      context.beginPath();
      context.arc(80,80,8,0,Math.PI*2,true);
      context.closePath();
      context.stroke();
      context.fill();
      
      context.fillStyle = "#0000FF";
      context.beginPath();
      context.arc(80,80,5,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      
      context.strokeStyle = "#000000";
      context.fillStyle = "#FFFFFF";
      context.beginPath();
      context.arc(120,80,8,0,Math.PI*2,true);
      context.closePath();
      context.stroke();
      context.fill();
      
      context.fillStyle = "#0000FF";
      context.beginPath();
      context.arc(120,80,5,0,Math.PI*2,true);
      context.closePath();
      context.fill();
      
      // nose
      context.fillStyle = "#000000";
      context.beginPath();
      context.moveTo(93,100);
      context.lineTo(100,93);
      context.lineTo(107,100);
      context.closePath();
      context.fill();

      // smile              
      context.strokeStyle = "#000000";
      context.beginPath();
      context.moveTo(70,110);

      context.quadraticCurveTo(100,150,130,110);       
      context.closePath();
      context.stroke();    
    }
  }
}

清单 6 继续使用 清单 5 中使用过的同一命名空间约定。其他画布的后续 JavaScript 必须遵循相同的约定。


结束语

在本文中,我介绍了 HTML5 并展示了如何实现一个 JSF 2 HTML5 canvas 复合组件,使 JSF 开发人员和页面创建者更便捷地使用 HTML5 canvas。我展示了如何将从 JSF 表达式语言获取的信息传递给与复合组件相关的 JavaScript,以及如何为 JavaScript 函数创建名称空间,以便相同命名的函数不会相互冲突。在下一期的 JSF 2 fu 中,我将向您展示如何实现另一个 HTML5 组件,以及如何将多个 HTML5 组件放在一个可重用的库中,您可以在一个 JAR 文件中将该库分发给其他开发人员。



下载

描述名字大小下载方法
本文样例代码j-jsf2fu-1010.zip49KBHTTP

关于下载方法的信息


参考资料

学习

讨论

  • 加入 My developerWorks 社区。查看开发人员推动的博客、论坛、组和 wikis,并与其他 developerWorks 用户交流。

关于作者

David Geary 的照片

David Geary 是一名作家、演讲家和顾问,也是 Clarity Training, Inc. 的总裁,他指导开发人员使用 JSF 和 Google Web Toolkit (GWT) 实现 Web 应用程序。他是 JSTL 1.0 和 JSF 1.0/2.0 专家组的成员,与人合作编写了 Sun 的 Web Developer 认证考试的内容,并且为多个开源项目作出贡献,包括 Apache Struts 和 Apache Shale。David 的 Graphic Java Swing 一直是关于 Java 的畅销书籍,而 Core JSF(与 Cay Horstman 合著)是关于 JSF 的畅销书。他还是 GWT Solutions 一书的作者。David 经常在各大会议和用户组发表演讲。他从 2003 年开始就一直是 NFJS tour 的定期演讲人,并且在 Java University 教授课程,三次当选为 JavaOne 之星。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=605051
ArticleTitle=JSF 2 简介: HTML5 复合组件,第 1 部分
publish-date=12272010
author1-email=david.mark.geary_cnnew1@gmail.com
author1-email-cc=

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。