如何显示不同尺寸的项 (HTML)
[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]
默认情况下,ListView 为列表中每个项分配相同的大小。使用网格布局时,你可以修改此行为,并通过使某些大小不同的项跨多个单元格来显示这些项。
你需要了解的内容
技术
先决条件
- 我们假设你可以创建和使用基本的 ListView 对象。有关 ListView 控件的介绍,请参阅快速入门:添加 ListView。
说明
关于 ListView 中的单元格和大小调整
研究代码之前,先了解 ListView 如何调整项大小很有帮助。
默认情况下,ListView 为其所包含的每个项分配了相同的大小。下面的 ListView 所包含的项大小均相同。
下面是相同的 ListView,但有一个单元格突出显示。
该单元格的大小取决于 ListView 中第一个项的大小。当 ListView 包含大小不同的项时,它仍将基于第一个项的大小分配单元格大小。因此,如果一个项大于其他项,则系统将对其进行裁剪以与其他 ListView 项的大小匹配。
你可以启用单元格跨并功能更改这一行为。从而,一个项即可占用多个单元格。在此示例中,启用了单元格跨并功能,较大的项占用 5 个单元格,而非一个单元格。
启用单元格跨并功能时,还可明确指定基础单元格的大小。我们建议将 ListView 中的每个项目大小设定为基础单元格的倍数。在下一个示例中,对较大的项进行了修改,使其高度为基础单元格的两倍,但宽度相同。
以下介绍如何创建包含三种不同大小的项的 ListView。
第 1 步:创建你的数据与 ListView
首先,创建一个数据源和一个 ListView。
在 JavaScript 文件中,为 ListView 定义一个数据源。此示例通过 JSON 对象数组创建 List,并使用 WinJS.Namespace.define 通过名为
DataExamples
的命名空间将其公开,从而使其可公开访问。该数据与我们在其他主题(例如快速入门:添加 ListView)中列举的示例类似,不过该数据增加了一个字段,即:
type
字段。 该字段有三个可能值:"smallListIconTextItem"、"mediumListIconTextItem" 和 "largeListIconTextItem"。在后续步骤中,我们将使用此字段分配 CSS 类,用于确定每个项的大小。(function () { "use strict"; var myCellSpanningData = new WinJS.Binding.List([ { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png", type: "smallListIconTextItem" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png", type: "mediumListIconTextItem" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png", type: "largeListIconTextItem" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png", type: "mediumListIconTextItem" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png", type: "smallListIconTextItem" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png", type: "smallListIconTextItem" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png", type: "mediumListIconTextItem" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png", type: "mediumListIconTextItem" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png", type: "smallListIconTextItem" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png", type: "smallListIconTextItem" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png", type: "smallListIconTextItem" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png", type: "smallListIconTextItem" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png", type: "smallListIconTextItem" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png", type: "smallListIconTextItem" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png", type: "mediumListIconTextItem" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png", type: "smallListIconTextItem" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png", type: "largeListIconTextItem" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png", type: "mediumListIconTextItem" } ]); WinJS.Namespace.define("DataExamples", { myCellSpanningData: myCellSpanningData }); })();
(如果编码时想使用此示例中所用的图像,你可以通过下载 ListView 项目模板示例获取这些图像。)
在 HTML 文件中,创建一个使用单元格跨并功能布局的 ListView。将其 itemDataSource 属性设置为你在上一步中所创建的数据源。
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, layout: { type: WinJS.UI.CellSpanningLayout } }" ></div>
第 2 步:定义基础单元格大小并启用单元格跨并功能
现在,我们需要定义基础单元格的大小。
若要让 ListView 使用单元格跨并功能布局,请创建一个 CellSpanningLayout 对象并使用该对象设置 ListView 控件的 layout 属性。若要启用单元格跨并功能并定义基础单元格的大小,请创建一个用于提供此信息的 groupInfo 函数,并使用此函数设置 CellSpanningLayout 对象的 groupInfo 属性。我们定义的 groupInfo 函数所返回的对象必须包含以下属性。
enableCellSpanning
设置为 true 以启用单元格跨并功能。默认值为 false。cellWidth
基础单元格的宽度。cellHeight
基础单元格的高度。
对于此示例,我们使用大小为 310×80 像素的基础单元格。
定义基础单元格的大小并启用单元格跨并功能的步骤
在创建数据所在的 JavaScript 文件中,创建一个 groupInfo 函数,以用于启用单元格跨并功能和定义大小为 310×80 像素的基础单元格。
// Enable cell spanning and specify // the cellWidth and cellHeight for the items var groupInfo = function groupInfo() { return { enableCellSpanning: true, cellWidth: 310, cellHeight: 80 }; };
使用 WinJS.Utilities.markSupportedForProcessing 使你的函数可在 HTML 中访问。
// Enable cell spanning and specify // the cellWidth and cellHeight for the items var groupInfo = function groupInfo() { return { enableCellSpanning: true, cellWidth: 310, cellHeight: 80 }; }; WinJS.Utilities.markSupportedForProcessing(groupInfo);
(默认情况下,鉴于安全考虑,函数和事件处理程序对 Windows JavaScript 库控件不可访问。使用 WinJS.Utilities.markSupportedForProcessing 函数可以覆盖此默认行为。这一操作假设你提供的 HTML 格式正确且可以由 WinJS 处理。有关详细信息,请参阅对基本应用编码。)
在函数中调用 WinJS.Utilities.markSupportedForProcessing 并不能使其可公开访问。我们将在下一步中介绍如何使其可公开访问。
通过命名空间使 groupInfo 函数可公开访问。此示例更新我们在第 1.1 步中创建的
DataExamples
命名空间。WinJS.Namespace.define("DataExamples", { groupInfo : groupInfo, myCellSpanningData: myCellSpanningData });
将你的 ListView 更新为使用 groupInfo 函数。
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, layout: { groupInfo: DataExamples.groupInfo, type: WinJS.UI.GridLayout } }" ></div>
第 3 步:定义占用单个单元格的项的大小
上面,我们已定义基础单元格大小,接下来我们可定义项的大小。我们在第一步定义数据时,包含了一个 type
字段,其中包含项可设置大小(小、中或大)的相关信息。我们可使用此信息来分配项大小。分配大小的最佳方法是使用 CSS 类。无论我们是使用模板函数还是 WinJS.Binding.Template,此方法均有效。
我们的基础单元格宽为 310 像素,高为 80 像素。每个项的总大小必须是基础单元格的倍数。基础单元格大小是指项加上该项的衬距、边距和边框所得出的大小:
以下是基础单元格大小的计算公式:
- 基础单元格宽度 = 项宽度 + 项水平衬距 + 项水平边距 + 项边框粗细
- 基础单元格高度 = 项高度 + 项垂直衬距 + 项垂直边距 + 项边框粗细
定义占用单个基础单元格的项的大小
下面我们定义最小项的大小。在 CSS 文件中,创建一个名为 "smallListIconTextItem" 的级联样式表 (CSS) 类。
.smallListIconTextItem { }
最小项仅占一个单元格。我们将项的宽度设置为 300px,高度设置为 70px,衬距设置为 5px。
.smallListIconTextItem { width: 300px; height: 70px; padding: 5px; overflow: hidden; background-color: Pink; display: -ms-grid; }
我们根据公式来检查下这些数字,以确保它们与我们的基础单元格大小匹配。
单元格宽度 = 项宽度 + 左衬距 + 右衬距 + 边框粗细 + 左边距 + 右边距 = 300 + 5px + 5px + 0 + 0 + 0 = 310
单元格高度 = 项高度 + 上衬距 + 下衬距 + 边框粗细 + 上边距 + 下边距 = 70px + 5px + 5px + 0 + 0 + 0= 80
它们与基础单元格大小匹配,因此,我们可以继续进行下一步。
第 4 步:定义跨 2 个或多个单元格的项的大小
确定跨一个或多个单元格的项的大小时,还必须考虑所跨单元格之间的 win-container
边距。例如,你有一个项水平跨一个单元格,但垂直跨两个单元格,则其总项大小包括第一个单元格的 win-container
下边距和第二个单元格的 win-container
上边距,如下所示。
以下是跨多个单元格的项的总项大小计算公式:
总项宽度 = number of cells * 基础单元格宽度 + (number of cells - 1) *(
win-container
左边距 +win-container
右边距)总项高度 = number of cells * 基础单元格高度 + (number of cells - 1) * (
win-container
上边距 +win-container
下边距)
提示 在默认情况下,win-container
边距为 5 像素。
定义垂直跨两个单元格的项的大小的步骤
使用我们的公式确定总项高度:
总项高度 = number of cells * 基础单元格高度 + (number of cells - 1) *(
win-container
上边距 +win-container
下边距)= 2 * 80 + (2-1) * (5 + 5) = 170创建用于指定大小项的 CSS 样式。此示例定义的项高度为 160 像素,衬距为 5 像素,因此其总高度为 160 + 5 + 5 = 170。由于项仅水平跨一个单元格,因此按照在第 3 步中创建的 CSS 类
smallListIconTextItem
设置相同的宽度和衬距。.mediumListIconTextItem { width: 300px; height: 160px; padding: 5px; overflow: hidden; background-color: LightGreen; display: -ms-grid; }
定义垂直跨三个单元格的项的大小
使用我们的公式确定总项高度:
总项高度 = number of cells * 基础单元格高度 + (number of cells - 1) *(
win-container
上边距 +win-container
下边距)= 3 * 80 + (3-1) * (5 + 5) = 260创建用于指定大小项的 CSS 样式。此示例定义的项高度为 250 像素,衬距为 5 像素,因此其总高度为 250 + 5 + 5 = 260。
.largeListIconTextItem { width: 300px; height: 250px; padding: 5px; overflow: hidden; background-color: LightBlue; display: -ms-grid; }
第 5 步:为 CellSpanningLayout 创建项大小函数
除了 groupInfo 函数外,CellSpanningLayout 还需要公开 itemInfo 函数,该函数可确定如何在数据源中调整不同“类型”的项的大小。itemInfo 函数需要返回 JavaScript 对象,该对象包含以下属性:
在 ListView 中定义单独项的大小的步骤
在创建数据所在的 JavaScript 文件中,创建一个 itemInfo 函数,该函数可返回从数据源检索的项,并返回该项的对应大小和高度。
// Item info function that returns the size of a cell spanning item var itemInfo = WinJS.Utilities.markSupportedForProcessing(function itemInfo(itemIndex) { var size = { width: 310, height: 80 }; // Get the item from the data source var item = DataExamples.myCellSpanningData.getAt(itemIndex); if (item) { // Get the size based on the item type switch (item.type) { case "smallListIconTextItem": size = { width: 310, height: 80 }; break; case "mediumListIconTextItem": size = { width: 310, height: 170 }; break; case "largeListIconTextItem": size = { width: 310, height: 260 }; break; default: } } return size; });
itemInfo 由对 WinJS.Utilities.markSupportedForProcessing 的调用包装,以使该函数在 HTML 中可访问。
通过命名空间公开 itemInfo 函数来使其可公开访问。本示例更新我们在第 1.1 步中创建的
DataExamples
命名空间。WinJS.Namespace.define("DataExamples", { myCellSpanningData: myCellSpanningData, groupInfo: groupInfo, itemInfo: itemInfo });
将你的 ListView 更新为使用 itemInfo 函数。
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, layout: { groupInfo: DataExamples.groupInfo, itemInfo: DataExamples.itemInfo, type: WinJS.UI.CellSpanningLayout } }" ></div>
第 6 步:创建模板
最后一步是创建使用我们刚定义的 CSS 类的模板或模板函数。下面,我们将介绍如何创建 WinJS.Binding.Template 和模板函数。
方案 A:使用 WinJS.Binding.Template
在 HTML 中,定义 WinJS.Binding.Template。
<div id="myItemTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div> <img src="#" class="regularListIconTextItem-Image" data-win-bind="src: picture" /> <div class="regularListIconTextItem-Detail"> <h4 data-win-bind="innerText: title"></h4> <h6 data-win-bind="innerText: text"></h6> </div> </div> </div>
回想我们在第 1.1 步中定义数据的方法和时间,该数据中有一个
type
属性用于指定将为每个项分配哪个 CSS 类。现在,我们即可使用该数据。在项的根元素上,将类名绑定到数据的type
字段的值。<div id="myItemTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div data-win-bind="className: type"> <img src="#" class="regularListIconTextItem-Image" data-win-bind="src: picture" /> <div class="regularListIconTextItem-Detail"> <h4 data-win-bind="innerText: title"></h4> <h6 data-win-bind="innerText: text"></h6> </div> </div> </div>
注意 在该示例中,绑定到 className,而非 class。正因如此,即使你在 HTML 中使用的是 "class",但备份 JavaScript 属性仍命名为 "className"。当应用处理 data-win-bind 属性时,会通过 JavaScript 调用分配绑定值。
这意味着,如果 HTML 属性名称和备份 JavaScript 属性名称不同,则设置 data-win-bind 时仍应使用 JavaScript 属性。
通过将 ListView 的 itemTemplate 属性设置为模板的 ID,将其更新为使用模板。
<div id="listView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, itemTemplate: select(#'myItemTemplate'), layout: { groupInfo: DataExamples.groupInfo, itemInfo: DataExamples.itemInfo, type: WinJS.UI.CellSpanningLayout } }"></div
如有必要,可使用模板函数代替 WinJS.Binding.Template。通过使用模板函数,你可以更灵活地确定如何生成 HTML 和分配大小。
方案 B:使用模板函数
在 JavaScript 文件中,定义模板函数。 你可以将此代码添加到包含数据的同一文件中,或者也可以将其添加到不同文件中。只需确保包含 ListView 的页面引用此文件即可。
此示例对每个项都使用
type
数据,以为其分配用于确定其大小的 CSS 类。var myCellSpanningJSTemplate = function myCellSpanningJSTemplate(itemPromise) { return itemPromise.then(function (currentItem) { var result = document.createElement("div"); // Use source data to decide what size to make the // ListView item result.className = currentItem.data.type; result.style.overflow = "hidden"; // Display image var image = document.createElement("img"); image.className = "regularListIconTextItem-Image"; image.src = currentItem.data.picture; result.appendChild(image); var body = document.createElement("div"); body.className = "regularListIconTextItem-Detail"; body.style.overflow = "hidden"; result.appendChild(body); // Display title var title = document.createElement("h4"); title.innerText = currentItem.data.title; body.appendChild(title); // Display text var fulltext = document.createElement("h6"); fulltext.innerText = currentItem.data.text; body.appendChild(fulltext); return result; }); };
在函数中调用 markSupportedForProcessing,以便可通过标记进行访问。
WinJS.Utilities.markSupportedForProcessing(myCellSpanningJSTemplate);
使用 WinJS.Namespace.define 使函数可公开访问。
WinJS.Namespace.define("Templates", { myCellSpanningJSTemplate: myCellSpanningJSTemplate });
在 HTML 中,通过将 ListView 的 itemTemplate 属性设置为模板函数的名称,将其更新为使用模板函数。
<div id="myListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: DataExamples.myCellSpanningData.dataSource, itemTemplate: Templates.myCellSpanningJSTemplate layout: { groupInfo: DataExamples.groupInfo, itemInfo: DataExamples.itemInfo, type: WinJS.UI.CellSpanningLayout } }" ></div>
无论使用哪种模板方法,运行应用时,ListView 都将显示多种大小的项。
备注
编辑项
若要在已启用单元格跨并功能的 ListView 中更改项,则更改时需调用 ListView.recalculateItemPosition。
- 如果数据源是 WinJS.Binding.List,则进行编辑后(例如调用 List.push 或 List.splice 后)立即调用 recalculateItemPosition。
- 如果数据源是自定义 VirtualizedDataSource,则先调用 beginEdits,进行编辑,然后依次调用 recalculateItemPosition 和 endEdits。