WML的基本情况
用于WAP的标记语言就是WML(Wireless Markup Language)。
WML的语法跟XML一样,WML是XML的子集。
HTML、XML和WML的文件有很多相似之处,这样网页开发者在过去10年中所学的东西今天依然适用。
WML页面文件的后缀是 *.WML,就象HTML的 *.HTML后缀。
XML规定定义了一个规范的XML文件的规格。任何违反这个规定的WML文件会出错。WML文件通常使用XML解释器起来解释。
建立网页制作环境
WML文件本身就是文本文件,所以编辑不成问题,顺手的编辑器都可以用。
当然,由于目前的浏览器还都不能显示WML页面,而我们又不能总在手机上进行测试(速度太慢),所以需要模拟器。现在象NOKIA、ERICSSON、 MOTOROLA等手机制造商都生产了相应的产品,你只要下载就行了。当然除了模拟器以外,还需要图形制作转换器(用来制作WAP格式的图形文件)、字符转码器(汉字〈-〉UNICODE)等等,“资源室-〉开发工具”都有,一并下载吧。
WML文件结构
WML 的页面通常叫做桌面(DECK),由一组互相链接的卡片(CARD)组成。当移动电话访问一个WML页面的时候,页面的所有CARD都会从WAP服务器下载到设备里。CARD之间的切换由电话内置的计算机处理,不需要再到服务器上取信息了。CARD里可以包含文本、标记、链接、输入控制、任务(TASK)、图像等等。CARD之间可以互相链接。
文档的实体包含在<wml>...</wml>标记中,文档里每个CARD又包含在<card>...</card>标记中,然后实际的文字段落则包含在<p>...</p>标记中。
简单例子:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="HELLO" title="HELLO">
<p>
Hello world!
</p>
</card>
</wml>
显示结果如下:
------ HELLO ------
Hello World!
DECK里面各个组成部分的具体解释在本教程的其他部分有说明。
WML字符集
WML是XML的子集,继承了XML的字符集设置。WML文档缺省的字符集是UTF-8。
要显示中文,有两种办法。最简单的办法就是在文档头使用encoding,即把第一行改为:
<?xml version="1.0" encoding="gb2312"?>
然而令人丧气的是,这种方法有些手机和模拟器并不支持(将来会的),所以目前第2种方法更普遍:不改变字符集设置,但是在写中文的时候采用UNICODE代表中文字符,如:
<b>通讯录</b>
代表:
通讯录
WML元素:标记(Tag)和属性
WML的主要内容是文本,由于标记会降低与手持设备的通讯速度,所以WML标准里仅仅使用了很少一部分。用于表格和图像的的标记几乎都被排除了。
与XML一样,在WML语言中,所有元素都放在符号"<" 和 ">"中,并且包含一个开始标志、一个结束标志和一个内容标志,或者使用自身结束的控制标记。就象这样:
<tag>内容</tag> 例如:<p>Hello world!</p>
或
<tag/> 例如:<br/> 和 <go href="#done"/>
WML同样支持在标志中标出属性。属性是标志的附加信息,与元素的内容不一样,它并不在屏幕上显示出来。属性通常在元素的开始标志后指定。如上面最后一个例子。
由于WML是XML的一种应用,因此所有的WML标记和属性都是大小写敏感的(<wml>跟<WML>完全不同),而且所有的标记都必须正确地结束。WML要求属性的值必须放在双引号或单引号内。单引号可放在属性标志内或双引号内。字符亦可作为属性的值。
WML注释
XML支持这样的注释格式:
<!这句话你在手机上看不到-->
这些注释在浏览器中并不显示出来。
WML不支持嵌套元素注释。
链接(URL)
WML外部引用方式跟HTML相同
http://www.wap86.net/great.wml 或
http://www.wap86.net/great.wml#login
内部引用,如果next是当前DECK中的一个CARD时,可以用这种方式:
#next
提供链接功能的WML元素有2个:<go>(参见任务)和<anchor>(参见事件)。
CDATA
XML支持CDATA的概念,以显示不需要解释的文本。下面的例子使用CDATA元素在WML页面中显示WML命令文本。
<! [CDATA [ <card name="bogus"><p>this is data</p></card> ] ]>
浏览器窗口将显示如下内容:
<card name="bogus"><p>this is data</p></card>
WML变量
格式 替换文本 ESCAPE转换
格式
象HTML一样WML可以定义变量,可以让你的页面设计更简单和富有逻辑性。变量格式如下显示:
$identifier
$(identifier)
$(identifier:conversion)
圆括号在变量带有空格时使用,第3种格式本节后面说明。
变量的优先权最高,所以当出现与变量符号相同的字符时,它将被认为是变量的标志。因此如果想在WML页面中显示$符号时必须在其后面再跟一个$符号。如下例:
You account has $$1650.00 in it.
变量名是由US-ASCII码、下划线和数字组成,并且只能以US-ASCII码开头。变量名大小写敏感。
WML变量没有类型,均表示为字符串。变量没有被赋值的时候,内容为空,即空字符串。可以在运行过程中改变它的值。
创建变量
创建变量最简单的方法是使用<setvar>元素,语法如下:
<setvar
name="string"
value="string" />
<setvar>只能在<go>、<prev>和<refresh>中使用(具体操作见本教程任务(TASK))。下例定义了一个叫vNAME的变量并赋值为“Jeff”:
<setvar name="vNAME" value="Jeff" />
另外,还可以在<input>、<select>和<postfield>中定义变量,参考本教程(任务(TASK)和数据输入)
替换文本
变量可以用作替换用途,但只能在文本类型(如显示字符、URL等)中使用。任何元素和属性都不能使用变量来替代。例如:
Hello, $vNAME.
将显示:
Hello, Jeff.
ESCAPE转换
前面说过变量可以用作替换用途,但是在URL中使用时,变量的内容必须遵守[RFC2396]标准。这个标准规定某些特殊字符在URL里使用的时候必须用特殊表示方法,即ESCAPE八进制表示。例如:
list.asp?id=3
在URL中要表示为:
list.asp%3fid=3
所以变量有可能需要标志为是否对其内容进行ESCAPE转换,变量定义就有如下几种特殊方式:
效果 表达方式1 表达方式2 表达方式3
对变量中ESCAPE字符进行转换 $(var:e) $(var:E) $(var:escape)
不进行ESCAPE转换 $(var:u) $(var:U) $(var:unesc)
变量中没有ESCAPE字符 $(var:n) $(var:N) $(noesc)
给变量进行ESCAPE转换是WML的默认方式,特殊字符ESCAPE码请参考本教程字符实体。
显示内容
段落与换行标记 文字样式标记 表格 图像
CARD里可以显示象文本、图像这样的内容。象HTML一样,WML也提供一些标记对内容进行排版。
段落与换行标记
<p>:段落标记,用来对段落进行分段。语法:
<p
aligh="left|right|center"
mode="wrap|nowrap";
xml:lang="STRING" >
aligh:文字对齐方式,左(left)、右(right)、居中(center)。
mode:文字超出屏幕时是否折行,各种终端处理方式不同。
xml:lang:显示语言编码。
<br>:行分隔标记,产生回车效果。语法:
<br
xml:lang="STRING" />
xml:lang:显示语言编码。
段落和换行的例子:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card title="Paragraphs">
<p>
This is a paragraph
</p>
<p>
This is a another<br/>with a line break
</p>
</card>
</wml>
显示结果如下:
------ Paragraphs ------
This is a paragraph
This is another
with a line break
标记 字体样式
<b> 粗体
<big> 大字体
<em> 强调字体
<i> 斜体
<small> 小字体
<strong> 加重强调字体
<u> 下划线字体
文字样式标记
WML提供了一系列文字样式的标记,如右表所示。不过WML鼓励大家尽量使用<strong>和<em>标记,因为某些WAP终端会忽略其他标记。
每个文字样式标记语法都一样,都有一个标志语言代码的xml:lang属性。
下面是一个例子:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card title="Formatting">
<p>
normal<br/>
<em>emphasized</em><br/>
<strong>strong</strong>
<br/> <b>bold</b> <br/>
<i>italic</i><br/>
<u>underline</u><br/>
<big>big</big><br/>
<small>small</small>
</p>
</card>
</wml>
在你的终端上可能会有这样的显示 (不过别太当真,前面说了有的终端会忽略文本样式标记):
list.asp%3fid=3
所以变量有可能需要标志为是否对其内容进行ESCAPE转换,变量定义就有如下几种特殊方式:
----- Formatting -----
normal
emphasized
strong
bold
italic
underline
big
small
表格
WML支持简单的表格标记<table>、<tr>和<td>。语法如下:
<table
align="L|R|C"
columns="NUMBER"
TITLE="STRING"
<tr>
<td> 内容... </td>
其他列...
</tr>
其他行...
</table>
在WML 里定义一个表格必须先指定列数,即columns属性。而在表格里就必须有相应数量的<td></td>标记对。因该注意的是, align(对齐)属性的内容只能是L(左对齐)、R(右对齐)和C(居中),跟其他标记不同(是不是有点怪?)。
下面的例子演示了表格的基本功能:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card title="SALARY">
<p>
<table columns="2">
<tr>
<td>NAME</td>
<td>PAYED</td>
</tr>
<tr>
<td>Johnson</td>
<td>$7810.11</td>
</tr>
<tr>
<td>Faye</td>
<td>$8912.00</td>
</tr>
</table>
</p>
</card>
</wml>
显示结果(注意WML里没有表格线):
------ SALARY ------
NAME PAYED
Johnson $7810.11
Faye $8912.00
图像
相对于多媒体泛滥的HTML网站,在资源紧张的WAP设备上显示图像就有点奇怪了。但是WML还是提供了图像显示的支持,毕竟一个设计精巧的图像会比一段话表达意思更清楚,或许占用空间更小。
WML支持WBMP(Wireless Bitmap)格式的图像,需要用特殊工具制作(下载:“资源室-〉开发工具”)。显示图像使用<img>标记,语法如下:
<img
alt="STRING"
src="STRING"
align="top|middle|bottom"
height="NUMBER"
width="STRING"
hspace="NUMBER"
vspace="STRING"
localsrc="STRING" />
alt: 图像无法显示时的替换文字。
src: 图像的url。
localsrc: 储存于ROM(或RAM)中图像的名字,各种终端支持不同。
align: 上下对齐方式。
height: 图像显示高度。
width: 图像显示宽度。
hspace: 图像左右的空白,以pixel数或百分比表示。
vspace: 图像上下的空白,以pixel数或百分比表示。
上面的属性只有alt和src是必须的。其他附加属性基本上仅仅用来指示用户终端,大多数情况下会被忽略。下面是个例子:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card title="Congratulation">
<p>
WAP86, <img src="wonderful.wbmp" alt="wonderful"/>!
</p>
</card>
</wml>
下面分别是在支持和不支持图像的终端上的显示结果:
--- Congratulation ---
WAP86,!
--- Congratulation ---
WAP86, wonderful!
桌面 (DECK)
XML说明 <wml> <head> <template>
<card> 不同浏览器指标
由于网络带宽以及某些WAP服务器DECK传输的限制,所以DECK越小越好,最好不要超过1.2K。如果你的需求很复杂,最好分成几个DECK来完成。
完整的WML文档结构如下:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<head>
.
. 头信息......
.
</head>
<template>
.
. 模板定义......
.
</template>
<card>
.
. 内容......
.
</card>
.
. 其他card(可有可无)......
.
</wml>
XML说明
DECK开头必须指明以下的XML类型声明:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
第一行指出XML版本,紧跟的文档类型(DOCTYPE)声明指出所引用的语言标准定义(wml_1.1.xml的内容可以察看本教程附录中的DTD说明)。
这两句的位置和顺序不可更改,而且中间不能有空行。
<wml>
语法:
<wml>标签和HTML中的<html>标签一样,用来表明这是一个WML的DECK。xml:lang属性来指定文档的语言,比如<wml xml:lang="zh">表示文档语言为中文。
<head>
<head>标签包含了该DECK的相关信息。<head>标签之间可以包含一个<access>标签和多个<meta>标签。
<access>语法:
<access
domain="STRING"
path="STRING" />
<access>相当于HTML中的<BASE>标签,指定该DECK的访问控制信息,它的两个属性是可选的。
domain:指定域,默认值为当前域。
path:指定路径,默认值为"/",即根目录。
<meta>语法:
<meta
http-equiv="STRING" | name="STRING"
forua="true|false"
content="STRING"
scheme="STRING" />
和HTML中<META>的类似,<meta>提供了该DECK的meta信息。
包括一下三种情况
name="name" UP.Link Server忽略meta数据
http-equiv="name" UP.Link Server将meta数据转为HTTP响应头(同HTML)
user-agent="agent" UP.Link Server直接将meta数据传给手机设备
content属性也是必选的,其内容根据属性而定。scheme属性目前尚不支持。forua为可选属性,指定在该wml文件传到客户端之前,<meta>标签是不是被中间代理删除(因为传输的协议可能改变),默认值为false。
目前支持的meta数据:
<meta http-equiv="Cache-Control" content="max-age=3600"/>指定DECK在手机内存缓存中的存储时间段,默认的为30天(除非内存耗尽),在该期间,手机对于访问过的DECK直接从缓存里调用。如果信息是对时间敏感的,可以用max-age指定DECK在缓存里的生存期,最小单位是秒,如果指定为0,则每次都需通过连接服务器来调用该DECK。
<meta user-agent="vnd.up.markable" content="false"/>和<meta user-agent="vnd.up.bookmark" content="指定的URL"/>类似于普通浏览器的书签功能。当用户将一个CARD做了书签后,手机浏览器首先用一个标记记录该CARD,这个标记默认的是<card>标签中的title属性(以后会讲到),然后当用户选择了该书签以后,浏览器就会打开被记录的URL。但是因为在默认的情况下,手机会记录所有的DECK,所以,一般<meta>被用来使手机不要记录当前的URL,即<meta user-agent="vnd.up.markable" content="false"/>。此外,如果要为书签指定不同于当前DRECk的URL,用<meta user-agent="vnd.up.bookmark" content="指定的URL"/>。
<template>
<template>元素声明一个DECK级的事件/请求,对DECK页面中所有CARD有效,当然某个CARD可以通过定义同名的事件来替代<template>声明中的事件处理。
语法:
<template
onenterforward="STRING"
onenterbackward="STRING"
ontimer="STRING" />
onenterforward:当用户通过<go>进入CARD时调入的链接。
onenterbackward:当用户通过<prev>退回CARD时调入的链接。
ontimer:<timer>事件激活时调入的链接。
例子(其中涉及的其他命令参考其他章节):
<wml>
<template>
<do type="options" name="do1" label="default">
<prev/>
</do>
</template>
<card id="first">
<!-该卡片将自动套用模块中定义的事件处理过程-->
…
</card>
<card id="second">
<!-使用空操作(noop)来屏蔽模块中定义的事件处理过程->
<do type="options" name="do1">
<noop/>
</do>
…
</card>
<card id="third">
<!-该卡片使用同名的事件处理替代模块中提供的事件处理->
<do type="options" name="do1" label="options>
<go href="/options"/>
</do>
</card>
</wml>
另外可以参看事件(EVENT)。
<card>
一个DECK可以包含多个CARD,每个CARD的内容可能不止一屏显示,注意DECK、CARD和屏幕显示范围的关系。一个CARD用<card>和</card>包含。
语法:
<card
id="STRING"
title="STRING"
newcontext="true|false"
ordered="true|false"
onenterforward="STRING"
onenterbackward="STRING"
ontimer="STRING"
xml:lang="STRING">
每个CARD元素可以有一个标号(ID)和标题(TITLE)。当然,这都不是必须的。
id:CARD的名字,在DECK中唯一,可用作URL已进行跳转。
title:CARD的标题,用户BOOKMARK一个CARD时的名字。该属性在某些用户终端上会显示出来。
newcontext:用来指示当跳转到本CARD时,用户终端(手机、模拟器等)是不是要清除以前保留的信息如变量、堆栈历史记录、终端状态等。默认值为FALSE。
ordered:表明该CARD里的内容是按固定的顺序显示,还是按用户的选择来显示。默认值是TRE。这点和HTML不同,CARD页面里的内容可以按一定的顺序显示,默认的是按线性顺序显示,即按代码的顺序,但是,要注意的是,以下三个标签必须按以下顺序来写<onevent> < timer> <do>,(这和以后要讲的“事件”有关)。这样做是为了方便填表单,当ordered设置为true时,如果一个表单的内容不能在一屏里显示完,就分成多屏显示;当ordered设置为false时,手机可以显示一个概要CARD来总结有效的选项,用户可以从中选取表单选项来填写。
onenterforward:当用户通过<go>进入CARD时调入的链接。
onenterbackward:当用户通过<prev>退回CARD时调入的链接。
ontimer:<timer>事件激活时调入的链接。
Nokia 7110> Ericsson R320> Ericsson R380> UP.Browser>
CARD>最大字节数> 1397 >字节> 3000 >字节> 3800 >字节> 1492 >字节>
>图象最大字节数> 1397 >字节> 3000 >字节?> 3800 >字节?> 1492 >字节?>
>显示行数> 4 >(含标题行)> 5> 7> >视终端不同> S3568i:6>
>显示列数> 19> 14> >不知道> >视终端不同>
>显示宽度(象素)> 95> 101> 304> >视终端不同>
>显示高度(象素)> 45> 52> 98> >视终端不同>
>象素比例> 1:1.25> >不知道> 1:1.23> >视终端不同>
>字体> >无> Small, Bold, Emphasis, Strong> Small, Big, Bold, Italic, Emphasis, Strong> Small, Big, Bold>
>文字位置> >没有,强制居左> >居左、居中、居右> >居左、居中、居右、段落缩进> >不知道>
>图象位置> >强制居左> >不知道> >不知道> >不知道>
>表格支持> >不支持> >最多5x5>格> >不知道> >不知道>
>触摸屏> >无> >无> >有> >无>
>图标键> >无> >无> >有> >不知道>
>在WML>页面内拨号> ">提取号码" >功能> >通过WTAI>支持> >通过WTAI>支持> 3.1>以上版本的浏览器支持>
>输入> >在行内输入> >在设置位置输入> >在设置位置输入> >不知道>
>输入格式> >只有大小写> >不知道> >不知道> >不知道>
>图形链接> >无> >有> >有> >不知道>
>链接格式> >在行内> >在设置位置> >在设置位置> >不知道>
DECK>下载顺序> >文字,然后图形,再开始选择性计时> >文字,开始选择性计时,然后是图(结果有可能是CARD>还未下载完毕就已过时)> >不知道> >
HTTP Redirect> >支持> >支持> >支持> >支持>
任务(TASK)
<noop> <prev> <refresh> <go>
前面我们已经讲过如何在WML中显示内容。不过任何程序员都知道,没有结构和进程就没有程序。在WML中定义进程有很多方法,最简单的就是任务。
WAP1.1定义了几种类型的任务,任务通过对事件(有关事件的详细解释见下一章事件)的响应改变程序的运行顺序。有四种WML任务: <noop>、 <prev>、 <refresh>和 <go>。
<noop>
这个任务不做任何事情,一般用于屏蔽DECK级事件(参见桌面和事件),语法非常简单:
<noop/>
<prev>
当用户激活该任务时,终端就转回上次用户访问过的URL。语法如下:
<prev>
<setvar>
......
</prev>
如果<prev>中包含了<setvar>元素,就会优先处理。下面例子定义了一个只有Back按钮的的DECK,按下以后会返回前面看过页面。
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card>
<p>
<anchor>
Back
<prev/>
</anchor>
</p>
</card>
</wml>
<refresh>
当用户激活该任务时,就执行一个刷新过程。如果这个任务里使用<setvar>定义了变量,变量值将被重新设置。语法如下:
<prev>
<setvar>
......
</prev>
如果当前CARD含有<timer>元素,那么在刷新时<timer>优先启动。
下面的例子定义在屏幕刷新时重设firstname、lastname和age变量。
<do type="refresh">
<refresh>
<setvar name="firstname" value="david">
<setvar name="lastname" value="smith">
<setvar name="age" value="29">
</refresh>
</do>
<go>
当用户激活该任务时,就引导用户去WML中指定URL,可以是服务器上其他的DECK,也可以是本DECK中其他的CARD。语法如下:
<go
accept-charset="STRING"
href="URL"
method="post|get" sendreferer="true|false" >
<postfield>, <setvar>......
</go>
href:必选属性,指向一个合法URL。如果是其他的DECK,则该DECK的第一个CARD会显示出来。如果是本DECK中的其他CARD,而历史堆栈里保存的是最新数据的话,则堆栈保持不变,直接调入该CARD。
sendreferer:如果为true,用户主体信息(USER AGENT)必须传送给WAP网关。传送时使用HTTP的提交头信息,即尽可能简短的相对URL。这个属性可以用来给服务器控制存取URL的权力。默认值为false。
Method:值必须为get或post。分别用来产生HTTP的GET和POST请求。若为get,则在URL中列出参数,例如:“http://www.wap86.net/bob.cgi?argone=one”;若为post,则数据在请求内部传送,不需要在URL中列出。
Accept-charset:指定字符集名称列表,服务器在接受<go>的时候必须接受这个编码规则。默认值为unknown。具体内容这里不作解释,在以后的高级教程中描述。
下面是一个简单的例子:
<go href="../topic.wml" sendreferer="true">
<postfield>
<postfield>并不是一个任务,但是跟<go>任务有关,所以在这里介绍。它用来定义“名称/值”对以便通过<go>向服务器发送HTTP请求。语法如下:
当用户激活该任务时,就引导用户去WML中指定URL,可以是服务器上其他的DECK,也可以是本DECK中其他的CARD。语法如下:
下面是一个简单的例子:
<postfield name="STRING" value="STRING" />
当一个含有<postfield>的任务被执行的时候,终端要完成这样一个过程:
1、识别“名称/值”对并准备参数变量;
2、参数变量转换成正确的字符集;
3、根据URL的ESCAPE规则对参数进行ESCAPE转码,编译成application/x-www-form-urlencoded的MIME类型;
4、根据method指定的请求模式提交任务。
下面的例子演示get模式的用法:
<go href="../news.asp" sendreferer="true" method="get">
<postfield name="newstype" value="technology"/>
<postfield name="newstext" value="wml"/>
</go>
服务器将收到这样的get请求:
GET ../news.asp?newstype=technology&newstext=wml HTTP/1.1
.
. 其他HTTP头信息
.
如果把前面的请求模式改成post,则同样的<go>任务产生的这样的post请求:
POST../news.asp HTTP/1.1
content-type="xxx-urlencoded" .
. 其他HTTP头信息
.
newstype=technology&newstext=wml