列表框

组件: listboxlistitem listcell listheadlistheader

list box 用于显示列表中的若干项目。用户可以从列表中选取某一项目。最简单的形式如下。这是一个单列单选择的列表框。

<listbox>
   <listitem label="Butter Pecan"/>
   <listitem label="Chocolate Chip"/>
   <listitem label="Raspberry Ripple"/>
</listbox>

Listbox有两种模型:defaultselect。若使用了select,就会产生HTML的SELECT标签。

<listbox mold="select">...</listbox>

注意: 若为"select"模型, 则行数为"1", 且并没有项目被标明选中,浏览器就像第一行被选中一样显示listbox。最坏的情况事,若用户选中了此事例中的首项目,则不会发出onSelect事件。为了避免这种困惑,开发人员至少要为mold="select"rows="1" 选择一个项目。

除了标签,你可以使用setValue方法为每个项目指派一个特定应用程序值。

无鼠标输入 listbox

  1. UPDOWN, 上下移动选中的列表项目。

  2. PgUpPgDn ,以一页的步长上下移动选中项。

  3. HOME ,选中首行,END ,选中末行。

  4. Ctrl+UPCtrl+DOWN ,上下移动列表项目的聚焦但并不改变选中项目。

  5. SPACE,选中聚焦项目。

多列列表框

List box也支持多列。当用户选中以一个项目时,整行都会被选中。

为了指定一个多列列表,你需要指定listcell组件作为每个listitem(作为一行)的列。

<listbox width="200px">
   <listitem>
      <listcell label="George"/>
      <listcell label="House Painter"/>
   </listitem>
   <listitem>
      <listcell label="Mary Ellen"/>
      <listcell label="Candle Maker"/>
   </listitem>
   <listitem>
      <listcell label="Roger"/>
      <listcell label="Swashbuckler"/>
   </listitem>
</listbox>

栏头

通过使用listheadlistheader可以指定栏头,如下[40]。除了标签,通过使用image属性你可以指定一张图像作为栏头。

<listbox width="200px">
   <listhead>
      <listheader label="Name"/>
      <listheader label="Occupation"/>
   </listhead>
...
</listbox>

栏尾

通过使用listfootlistfooter,你可以指定栏尾,如下。注意listheadlistfoot的顺序是不匹配的。每次listhead实例被添加到一个listbox时,它必须为第一个子组件,而listfooter实例为最后一个子组件。

<listbox width="200px">
   <listhead>
      <listheader label="Population"/>
      <listheader align="right" label="%"/>
   </listhead>
   <listitem id="a" value="A">
      <listcell label="A. Graduate"/>
      <listcell label="20%"/>
   </listitem>
   <listitem id="b" value="B">
      <listcell label="B. College"/>
      <listcell label="23%"/>
   </listitem>
   <listitem id="c" value="C">
      <listcell label="C. High School"/>
      <listcell label="40%"/>
   </listitem>
   <listitem id="d" value="D">
      <listcell label="D. Others"/>
      <listcell label="17%"/>
   </listitem>
   <listfoot>
      <listfooter label="More or less"/>
      <listfooter label="100%"/>
   </listfoot>
</listbox>

下拉列表

通过指定select模型及单行可以创建一个下拉列表。注意不能为下拉列表指定多列。

<listbox mold="select" rows="1">
   <listitem label="Car"/>
   <listitem label="Taxi"/>
   <listitem label="Bus" selected="true"/>
   <listitem label="Train"/>
</listbox>

多选

当用户点击一个列表项目时,这个项目会被选中,且onSelect事件会被送回服务器以通知应用程序。通过将multiple属性设置为 true,你可以控制一个listbox 是否允许选中多行。默认为false。

滚动列表框

若指定了rowsheight属性且不足以显示所有项目时, listbox 会变为滚动的。

<listbox width="250px" rows="4">
   <listhead>
      <listheader label="Name" sort="auto"/>
      <listheader label="Gender" sort="auto"/>
   </listhead>
   <listitem>
      <listcell label="Mary"/>
      <listcell label="FEMALE"/>
   </listitem>
   <listitem>
      <listcell label="John"/>
      <listcell label="MALE"/>
   </listitem>
   <listitem>
      <listcell label="Jane"/>
      <listcell label="FEMALE"/>
   </listitem>
   <listitem>
      <listcell label="Henry"/>
      <listcell label="MALE"/>
   </listitem>
   <listitem>
      <listcell label="Michelle"/>
      <listcell label="FEMALE"/>
   </listitem>
</listbox>

rows属性

rows属性用于控制显示多少行。若将其设置为0,listbox会将自身大小调整到容纳所有的项目。

可变列表头

就像columns,你可以将listheadsizable属性设为true,以允许用户改变列表头的宽度。类似的当用户改变列宽时onColSize事件会被送出。

分页列表框

就像grid,指定paging模型,你就可以使用多页来呈现listbox的较长内容。类似的,你可以控制每页显示多少项目,是否使用额外的paging组件,以及当选中某一页面时是否定制行为。参考网格一节获取细节。

排序

Listbox直接支持列表项目的排序。有几种方式来启动某一列的排序。最简单的方式是将listheader 的sort属性设为auto,如下。然后,与listheader 关联的列会基于指定列的每个列表元素的标签排序。

<zk>
   <listbox width="200px">
      <listhead>
         <listheader label="name" sort="auto"/>
         <listheader label="gender" sort="auto"/>
      </listhead>
      <listitem>
         <listcell label="Mary"/>
         <listcell label="FEMALE"/>
      </listitem>
      <listitem>
         <listcell label="John"/>
         <listcell label="MALE"/>
      </listitem>
      <listitem>
         <listcell label="Jane"/>
         <listcell label="FEMALE"/>
      </listitem>
      <listitem>
         <listcell label="Henry"/>
         <listcell label="MALE"/>
      </listitem>
   </listbox>
</zk>

sortAscendingsortDescending 属性

若你想用不同的方式为列表项目排序,可以为sortAscending/sortDescending属性指派一个java.util.Comparator实例。一旦指定了,列表项目会使用你指派的比较器以升序或降序存储。

调用值为auto的sort属性实际上是自动为sortAscedingsortDescending指定了两个比较器。你可以重定义它们中的任意一个通过为其指派另一个比较器。

例如,假定你想基于列表项目的值排序,而不是列表元素的标签,可以按如下方式为这些属性指派以一个ListitemComparator的实例。

<zscript>
   Comparator asc = new ListitemComarator(-1, true, true);
   Comparator dsc = new ListitemComarator(-1, false, true);
</zscript>
<listbox>
   <listhead>
      <listheader sortAscending="${asc}" sortDescending="${dsc}"/>
...

sortDirection属性

sortDirection属性控制是否在客户端显示一个图标,以指明特定列的排列顺序。若列表项目在添加到listbox前已排好序,你需要明确设置这个属性。

<listheader sortDirection="ascending"/>

然后,只要你为相应的listheader指派了比较器listbox会自动维护。

onSort事件

当你为listheader 至少指派了一个比较器时,若用户点击了它,onSort事件就会被送至服务器。 listheader 实现了一个监听器来自动处理排序。

若你喜欢受手动处理,可以将你的监听器添加到listheader的onSort事件。为了阻止默认的监听器调用sort方法,你必须调用stopPropagation方法来阻止接收事件。另外,你可以重定义sort方法,见下文。

sort方法

sort方法是默认的onSort事件监听器的最底层实现。若你想使用Java代码为列表项目排序这也是很有用的。例如,你可以在添加项目(假定并未排好序)之后调用此方法。

new Listem("New Stuff").setParent(listbox);
if (!"natural".header.getSortDirection())
   header.sort("ascending".equals(header.getSortDirection()));

默认的排序算法为快速排序(使用org.zkoss.zk.ui.Components类的sort方法)。你可以将其用自己的实现来重定义,或像前一章节描述的那样监听onSort事件。

提示: 为大量实况数据(live data)排序或许会显著的降低性能。最好是侦听(intercept) onSort事件或sort方法以有效处理排序。参考下面的为实况数据排序一节。

特殊属性

checkmark属性

checkmark属性控制是否在每个listitem前显示一个checkbox 或radio按钮。

在下面的例子中,当你将一个listitem 从左边的listbox 移到右边的listbox时,会自动添加一个checkbox。反过来checkbox会被移去。

<hbox>
   <listbox id="src" rows="0" multiple="true" width="200px">
      <listhead>
         <listheader label="Population"/>
         <listheader label="Percentage"/>
      </listhead>
      <listitem id="a" value="A">
         <listcell label="A. Graduate"/>
         <listcell label="20%"/>
      </listitem>
      <listitem id="b" value="B">
         <listcell label="B. College"/>
         <listcell label="23%"/>
      </listitem>
      <listitem id="c" value="C">
         <listcell label="C. High School"/>
         <listcell label="40%"/>
      </listitem>
      <listitem id="d" value="D">
         <listcell label="D. Others"/>
         <listcell label="17%"/>
      </listitem>
   </listbox>
   <vbox>
      <button label="=&gt;" onClick="move(src, dst)"/>
      <button label="&lt;=" onClick="move(dst, src)"/>
   </vbox>
   <listbox id="dst" checkmark="true" rows="0" multiple="true" width="200px">
      <listhead>
         <listheader label="Population"/>
         <listheader label="Percentage"/>
      </listhead>
      <listitem id="e" value="E">
         <listcell label="E. Supermen"/>
         <listcell label="21%"/>
      </listitem>
   </listbox>
   <zscript>
void move(Listbox src, Listbox dst) {
   Listitem s = src.getSelectedItem();
   if (s == null)
      Messagebox.show("Select an item first");
   else
      s.setParent(dst);
}
   </zscript>
</hbox>

注意若multiple属性为 false,则会显示radio 按钮,如右图所示。

vflex属性

vflex属性控制是否在垂直方向增长或缩小以适合指定空间。即所谓的垂直柔性(vertical flexibility)。例如,如果列表太长以至于不适合浏览器窗口,此属性会缩小列表的高度以控制整个列表在浏览器内可见。

若指定了rows属性则此属性会被忽略。

maxlength 属性

maxlength属性定义了浏览器端可见字符的最大允许字节数。通过设置这个属性,你可以将listbox变窄。

实况数据

就像grid[41],listbox支持live data。使用了实况数据,开发人员可以将数据从视图分离。换句话说,开发人员仅需要实现org.zkoss.zul.ListModel接口提供数据,而不用直接操作listbox。好处有以下两点,

  • 易于使用不同的视图来显示相同的数据。

  • listbox仅在其可见时才会将数据送至客户端。在数据量巨大时可以减少大量的网络流量(network traffic)。

使用实况数据需要经过三步,

  1. ListModel形式准备好数据。ZK有一个称为org.zkoss.zul.SimpleListModel的具体实现,用于显示一个数组对象。

  2. 实现org.zkoss.zul.ListitemRenderer接口用于将一个数据项目送至listbox的一个列白哦项目。

    • 这是可选的。若为指定,默认的渲染器(renderer)会启,并将数据送至第一列。

    • 你可以实现不同的渲染器(renderer),这样可以在不同的视图中显示相同的数据。

  3. model属性中指定数据,并且可以选择在itemRenderer属性指定渲染器(renderer)。

在下面的例子中,我们准备了一个strset列表模型(list model),通过model属性将其指派给一个listbox。然后,listbox会处理余下的工作。

<window title="Livedata Demo" border="normal">
   <zscript>
      String[] data = new String[30];
      for(int j=0; j &lt; data.length; ++j) {
         data[j] = "option "+j;
      }
      ListModel strset = new SimpleListModel(data);
   </zscript>
   <listbox width="200px" rows="10" model="${strset}">
      <listhead>
         <listheader label="Load on demend"/>
      </listhead>
   </listbox>
</window>

为实况数据排序

若你允许用户为一个提供实况数据的listbox排序,你可以实现org.zkoss.zul.ListModelorg.zkoss.zul.ListModelExt接口。

class MyListModel implements ListModel, ListModelExt {
   public void sort(Comparator cmpr, boolean ascending) {
      //do the real sorting
      //notify the listbox (or grid) that data is changed by use of ListDataEvent
   }
}

当用户向listbox发出排序请求时,listbox将会调用ListModelExtsort方法为数据排序。换句话说,排序是由列表模型处理的,而不是listbox。

排好序之后,列表模型会调用org.zkoss.zul.event.ListDataListener实例(通过addListDataListener方法注册到listbox)的onChange方法通知listbox。在大多数情况下,所有的数据通常会改变,所以列表模型通常发出下列事件:

new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1)

注:ListModelListModelExt的实现与视觉表现是独立的。换言之,它可以被用于grid ,listbox及其它支持ListModel的任意组件。

换言之,为了获得最大的灵活性,你应该假定不使用组件,而使用ListDataEvent通信。

包含按钮的列表框

理论上,listcell可以包含任意的其它组件,如下所述。

<listbox width="250px">
   <listhead>
      <listheader label="Population"/>
      <listheader label="Percentage"/>
   </listhead>
   <listitem value="A">
      <listcell><textbox value="A. Graduate"/></listcell>
      <listcell label="20%"/>
   </listitem>
   <listitem value="B">
      <listcell><checkbox label="B. College"/></listcell>
      <listcell><button label="23%"/></listcell>
   </listitem>   <listitem value="C">
      <listcell label="C. High School"/>
      <listcell><textbox cols="8" value="40%"/></listcell>
   </listitem></listbox>

注:

  1. 若使用grid更好,则不要使用listbox。它们的外观类似,但listbox应仅用于呈现可选项目的列表。

  2. 若listbox包括可编辑的组件,例如textboxcheckbox,则会引起用户的困惑。一个普遍的问题使,用户在一个未选中的项目内输入文本(A common question is what the text, that a user entered in a unselected item, means).

  3. 由于浏览器的限制,用户不能从文本框(text box)内选择一段文字。



[40] 在使用listhead 和 listheader的地方,此特性与XUL有一些不同。

[41] 此概念类似于Swing(javax.swing.ListModel)。