如何顯示大小不同的項目 (HTML)
[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]
根據預設,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);
(根據預設,為了安全性考量,適用於 JavaScript 的 Windows Library 控制項無法存取函式與事件處理常式。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 的 item-sizing 函式
除了 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 屬性設為範本的識別碼,即可進行更新以使用範本。
<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。