如何将 ListView 品牌化

[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]

将 Windows 应用商店应用品牌化中,我们介绍过如何在遵循 Microsoft 设计原则的同时将你的品牌的精华合并到应用中。 我们介绍过品牌设计的七个方面:颜色、图标、图像、网格、布局、徽标和版式。

下面我们将讨论如何使用这些技术自定义应用的登陆页面:

本主题展示如何从 Visual Studio“Grid App”模板启动以及如何通过修改此模板来创建类似如下的登陆页面:

Contoso 面包店登陆页面

Contoso 食品运输车应用

先决条件

何时使用 ListView

首先确定要在登陆页面上使用哪些控件。 有关 JavaScript 控件的 HTML 和 Windows 库的列表,请参阅控件列表

如果你希望将一个数据集合显示为一系列项(如电子邮件列表、搜索结果或要购买的商品目录),请使用 ListView

ListView 以列表或网格布局形式显示项。 大多数用来显示项的登陆页面都使用 ListView 控件的网格布局,因为它自动沿着水平方向溢出和滚动。 你可以通过修改用来显示 ListView 中各项的 itemTemplate 来自定义这些项。

CSS3 网格布局和 Windows 8 网格系统

网格系统是 Windows 8 外观的一个重要部分,有助于在不同的应用和功能之间实现视觉统一。 使用网格布局,可以分割页面上元素的空间,并将元素轻松对齐到网格。

在我们的示例中,我们还使用级联样式级别 3 (CSS3) 网格布局,该网格布局不同于 ListView 控件的网格布局。 CSS3 网格布局提供可用于广泛用途的通用网格样式,而 ListView 仅用于显示数据集合。使用 CSS3 网格布局,可以清晰地布置你的应用并将元素方便地对齐到网格。

法国 Contoso 面包店示例

让我们看一下默认的 Visual Studio Grid App 模板并将它与将 Windows 应用商店应用品牌化进行比较。 下面是 Grid App 模板的屏幕截图:

使用 Grid App 模板

让我们将此模板与将 Windows 应用商店应用品牌化中的法国 Contoso 面包店登录页面进行比较。 法国 Contoso 面包店按照可强调 Contoso 品牌的自定义方式布置其各个项。

Contoso 面包店登陆页面

尽管法国 Contoso 面包店登陆页面看上去与 Grid App 模板有很大的差别,但是,这一差别源自对 HTML/CSS 进行的少量修改。 在该示例中,我们假设这些项可与用户互动,而且选择一个项会将用户带到一个组明细页面,其中包括各种类型的蛋白杏仁饼干、杯形糕饼等。 使用网格布局的 ListView 是适合此登陆页面的控件。

若要将 Grid App 模板转换为 Contoso 登陆页面,我们需要更改项模板的大小,增加图像尺寸,并添加项描述。

  1. 在 Visual Studio 中,创建一个使用 Grid App 模板的新应用。

  2. 更新 groupedItems.html 中的 HTML itemtemplate。 对默认 itemtemplate 进行的主要更改是添加第三个标题元素来显示每个项描述。

        <div class="itemtemplate" data-win-control="WinJS.Binding.Template">
            <div class="item">
                <img class="item-image" src="#" 
                     data-win-bind="src: backgroundImage; alt: title" />
                <div class="item-text">
                    <h3 class="item-title" data-win-bind="textContent: title"></h3>
                    <h6 class="item-subtitle win-type-ellipsis" 
                        data-win-bind="textContent: subtitle"></h6>
                    <h6 class="item-detail" data-win-bind="textContent: description"></h6>
                </div>
            </div>
        </div>
    
  3. 现在我们更新 groupedItems.css 中的样式。对 groupedItems.css 进行的主要更改是将文本 div 移动到图像下方(而不是将它叠加到图像上方),并在网格中添加一个针对项明细元素的行。

    .groupeditemspage .groupeditemslist .win-horizontal.win-viewport .win-surface {
            margin-bottom: 60px;
            /* Decreased margin */
            margin-left: 35px;
            margin-right: 115px;
    }   
    .groupeditemspage .groupeditemslist .item {
           /* Changed row size and item size, centered text and changed text color */ 
            -ms-grid-columns: 1fr;
            -ms-grid-rows: 1fr 1280px;
            display: -ms-grid;
            height: 600px500px;
            width: 350px;
            text-align: center;
            color: rgb(160,160,160);
    
        }
    
            .groupeditemspage .groupeditemslist .item .item-image {
                   /* Increased image size and altered padding */
                height: 340px;
                width: 340px;
                padding: 0px 5px 20px 5px;
            }
    
            .groupeditemspage .groupeditemslist .item .item-text {
                /* Added a row to the grid and changed height and padding */
                -ms-grid-row: 2;
                -ms-grid-rows: 30px 21px 1fr;
                display: -ms-grid;
                padding: 30px 15px 2px 15px;
                height: 150px;
            }
    
                .groupeditemspage .groupeditemslist .item .item-text .item-title {
                    /* Changed font color */
                    -ms-grid-row: 1;
                    overflow: hidden;
                    font-size: 16pt;
                    color: rgb(200,200,200);
                }
    
                .groupeditemspage .groupeditemslist .item .item-text .item-subtitle {
                    -ms-grid-row: 2;
                }
                .groupeditemspage .groupeditemslist .item .item-text .item-detail {
                    /* All new CSS for the detail text */
                    -ms-grid-row: 3;
                    overflow:hidden;
                    padding-top: 20px;
                    height: 60px;
                    margin-left: 30px;
                    margin-right: 30px;
                }
    
  4. 从 data.js 中删除组标题。 (最方便的方法是只需删除每个组的标题。)。

进行这些更改后,你的应用现在看上去如下所示:

更新的应用

添加一些图像并更改背景和标题后,法国 Contoso 面包店登陆页面即创建完毕!

Contoso 食品运输车示例

在下一个示例中,我们将使用 Grid App 模板创建 Contoso 食品运输车登陆页面。

Contoso 食品运输车应用

Contoso 食品运输车登陆页面使用各种尺寸的引人注目的图像来吸引用户。

与上一个示例不同,此登陆页面需要在模板中增加一些 JavaScript 内容,其主要目的是添加用来为 ListView 中的项提供不同尺寸的逻辑。 而且,我们假设登陆页面中的项可与用户互动,而且选择一个项会将用户带到该项的详细信息视图。 使用网格布局的 ListView 是适合完成该任务的工具。 而且,我们使用 Grid App 模板作为起点。

要使用多尺寸的项,我们必须首先确定一个最小的基本单位。 我们使用此单位构建所有的网格项,因此,所有的网格项必须由该网格大小的倍数组成。 下图中各项的最小尺寸是“Near Me”部分中项的高度(大于为 80px)。 对于水平尺寸,灵活性更大。 为简化起见,我们对于水平尺寸也使用 80px。

下图显示基本单位(红色方框)与几个实际项的外观比较。

基本项的大小

在计算项大小时,每个项大小都必须等于基本单位的倍数加上单位之间的距离。 计算公式为:

item sizeₓ = m * base unit sizeₓ + (m -1) * item padding

item sizey = m * base unit sizey + (m -1) * item paddingy

其中,m 是正整数,x 和 y 分别表示项大小和项边距的 x 和 y 尺寸。

如果基本尺寸相对于项来说太小,它会降低应用的性能。 根据经验,在任何方向上,任何项的大小都不应当大于多个基本单位。

  1. 在 Visual Studio 中,创建一个使用 Grid App 模板的新应用。

  2. 在 groupedItems.html 中,创建一个名为 multisizebaseitemtemplate 的新项模板。 此项模板与默认的项模板大致相同,但是,在添加了“item-description”标题之后,我们除了可以在登陆页面上添加标题和副标题外,还可以包括项描述。

      <!-- Template tutorial HTML -->
        <div class="multisizebaseitemtemplate" data-win-control="WinJS.Binding.Template">
            <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
            <div class="item-overlay">
                <h4 class="item-title" data-win-bind="textContent: title"></h4>
                <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
                <h6 class="item-description" data-win-bind="textContent: description"></h6>
            </div>
        </div>
    
  3. 在 groupedItems.js 中,在 PageControl 定义 (ui.Pages.define) 前面创建一个名为 multisizeItemTemplateRenderer 的模板函数。

    我们使用此函数来呈现 ListView 项。此函数负责确定哪些项使用哪个项模板。在后面的步骤中,我们会为将它分配给 ListView 控件的 itemTemplate 属性。

     function multisizeItemTemplateRenderer(itemPromise) {
            return itemPromise.then(function (currentItem) {
                var content;
                // Grab the default item template used on the groupeditems page.
                content = document.querySelector(".multisizebaseitemtemplate");
                var result = content.cloneNode(true);
    
                // Change the CSS class of the item depending on the group, then set the size in CSS.
                switch (currentItem.groupKey) {
                    case "group1":
                        {
                            // For the first item, use the largest template.
                            if (currentItem.index == 0) {
                                result.className = "largeitemtemplate"
                            }
                            // Use the mediumlarge template for the second item
                            else if (currentItem.index == 2) {
                                result.className = "mediumlargeitemtemplate"
                            }
                            // Use the medium template for the third item, and any other items
                            else {
                                result.className = "mediumitemtemplate"
                            }
                            break;
                        }
                    default:
                        {
                            // Use the small template for the second group
                            result.className = "smallitemtemplate"
                        }
                }
                // Because we used a WinJS template, we need to strip off some attributes 
                // for it to render.
                result.attributes.removeNamedItem("data-win-control");
                result.attributes.removeNamedItem("style");
                result.style.overflow = "hidden";
    
                // Because we're doing the rendering, we need to put the data into the item.
                // We can't use data binding.
                result.querySelector(".item-image").src = currentItem.data.backgroundImage;
                result.querySelector(".item-title").textContent = currentItem.data.title;
                result.querySelector(".item-subtitle").textContent = currentItem.data.subtitle;
                result.querySelector(".item-description").textContent = currentItem.data.description;
                return result;
            });
        }
    
  4. 在 groupedItems.js 中,再次在页面定义外部添加 groupInfo 函数。该函数可告知 ListView 使用视图中的多尺寸项,并显示项的基本尺寸。基本尺寸是列表中显示的最小项。其他项必须为该尺寸的倍数,这样布局才能正常工作。

     function groupInfo() {
        return {
            enableCellSpanning: true,
            cellWidth: 80,
            cellHeight: 80
        };
     }
    
  5. 现在,我们需要将这些新函数绑定到 ListView 控件。

    1. 在 groupedItems.js 中,更改 _initializeLayout 函数以便可显示平的组列表。

      // Add the itemTemplate parameter as shown.
      _initializeLayout: function (listView, viewState, itemTemplate) {
          if (viewState === appViewState.snapped) {
              listView.itemDataSource = Data.groups.dataSource;
              listView.groupDataSource = null;
      
              // Add the following line of code.
              listView.itemTemplate = itemTemplate;
      
              listView.layout = new ui.ListLayout();
          } else {
      
                      listView.itemDataSource = Data.items.dataSource;
                      listView.groupDataSource = Data.groups.dataSource;
                      listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
      
              // Add the following two lines of code.
              listView.itemTemplate = multisizeItemTemplateRenderer;
              listView.layout = new ui.GridLayout({ groupInfo: groupInfo, groupHeaderPosition: "top" });
          }
      },
      
    2. 删除用于为 ListView 分配项模板的行,并进行此代码中的注释所指示的更改。

      ready: function (element, options) {
          var listView = element.querySelector(".groupeditemslist").winControl;
      
          // Add the next line of code to retrieve the item template. 
          var itemTemplate = element.querySelector(".itemtemplate");
      
          listView.groupHeaderTemplate = element.querySelector(".headerTemplate");
      
          listView.oniteminvoked = this.itemInvoked.bind(this);
              listView.itemTemplate = element.querySelector(".itemtemplate");
      
          // Change the last argument of the _initializeLayout function to itemTemplate.
          this._initializeLayout(listView, appView.value, itemTemplate);
          listView.element.focus();
       },
      
      // This function updates the page layout in response to viewState changes.
      updateLayout: function (element, viewState, lastViewState) {
          var listView = element.querySelector(".groupeditemslist").winControl;
      
          // Add the next line of code to retrieve the item template.
          var itemTemplate = element.querySelector(".itemtemplate");
      
          if (lastViewState !== viewState) {
              if (lastViewState === appViewState.snapped || viewState === appViewState.snapped) {
                  var handler = function (e) {
                      listView.removeEventListener("contentanimating", handler, false);
                      e.preventDefault();
                  }
                  listView.addEventListener("contentanimating", handler, false);
      
                  // Change this line to pass through the item template.
                  this._initializeLayout(listView, viewState, itemTemplate);
              }
          }
      },
      
      
  6. 接着,我们需要将这些项的样式添加到 groupedItems.css 中。 若要按照上一图中所显示的那样为这些项设置样式,我们需要针对登陆页面上 4 个不同的项模板使用 4 个 CSS 类。 将这 4 个类分别命名为 smallitemtemplatemediumitemtemplatemediumlargeitemtemplatelargeitemtemplate。 下一个 CSS 主要是相对于该图像放置叠加内容和文本,并相应地设置每个项的大小。 在某些情况下,某些元素呈折叠状态,因为并非所有的模板都使用项模板中的所有元素。 将此 CSS 添加到紧挨第一个 @media screen CSS 行的前面。

    /* Generic styling */
    .groupeditemspage .groupeditemslist .item-overlay {
        -ms-grid-row: 2;
    }
    .groupeditemspage .groupeditemslist .item-overlay .item-description {
        visibility:collapse;
    }
    
    /* Small item template */
    .groupeditemspage .groupeditemslist .smallitemtemplate {
        width: 440px;
        height: 80px;
        overflow: hidden;
    
    }
    .groupeditemspage .groupeditemslist .smallitemtemplate .item-image {
        height: 80px;
        width: 80px;
    }
    .groupeditemspage .groupeditemslist .smallitemtemplate .item-overlay {
       opacity: 0;
    }
    .groupeditemspage .groupeditemslist .smallitemtemplate .item-overlay .item-title {
        position: absolute; 
        top: -5px;
        padding-left: 90px;
        font-size: 11pt;
    }
    .groupeditemspage .groupeditemslist .smallitemtemplate .item-overlay .item-subtitle {
        position: absolute; 
        top: 15px;
        padding-left: 90px;
        font-size: 9pt;
    }
    .groupeditemspage .groupeditemslist .smallitemtemplate .item-overlay .item-description {
        position: absolute; 
        top: 35px;
        padding-left: 90px;
        font-size: 9pt;
        visibility: visible;
        width: 360px;
        overflow-wrap: normal;
        text-overflow: initial;
    }
    
    /* Medium item template */
    .groupeditemspage .groupeditemslist .mediumitemtemplate {
        width: 260px;
        height: 170px;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr 30px;
        display: -ms-grid;
        overflow: hidden;
    }      
    .groupeditemspage .groupeditemslist .mediumitemtemplate .item-overlay .item-title {
        padding-top: 5px;
        padding-left: 10px;
    }
    .groupeditemspage .groupeditemslist .mediumitemtemplate .item-overlay .item-title {
        font-size: 14px;
    }
    .groupeditemspage .groupeditemslist .mediumitemtemplate .item-overlay .item-subtitle {
        visibility: collapse;
    }   
    
    /* Medium-large item template */
    .groupeditemspage .groupeditemslist .mediumlargeitemtemplate {
        width: 260px;
        height: 350px;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr 30px;
        display: -ms-grid;
        overflow: hidden;
    }
    .groupeditemspage .groupeditemslist .mediumlargeitemtemplate .item-overlay .item-title {
        padding-top: 5px;
        padding-left: 10px;
        font-size: 14px;
    }
    
    .groupeditemspage .groupeditemslist .mediumlargeitemtemplate .item-overlay .item-subtitle {
        visibility: collapse;
    }   
    
    /* Large item template */
    .groupeditemspage .groupeditemslist .largeitemtemplate {
        width: 440px;
        height: 530px;
        overflow: hidden;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr 90px;
        display: -ms-grid;
    }
    .groupeditemspage .groupeditemslist .largeitemtemplate .item-overlay {
        -ms-grid-row: 2;
        -ms-grid-rows: 1fr 21px;
        display: -ms-grid;
        padding: 6px 15px 2px 15px;
    }
    .groupeditemspage .groupeditemslist .largeitemtemplate .item-subtitle{
        -ms-grid-row: 2;
    }
    
    
  7. @media screen and (-ms-view-state: fullscreen-landscape), screen and (-ms-view-state: fullscreen-portrait), screen and (-ms-view-state: filled) 规则中,将第一个 CSS 样式更改为下列样式。此 CSS 代码使叠加内容不透明,并删除 "item" 类。

     .groupeditemspage .groupeditemslist .item-overlay {
            background: rgba(0,0,0,1);
        }
    
  8. 将 groupedItems.css 中的第一个 .groupeditemspage .groupeditemslist .win-horizontal.win-viewport .win-surface 样式更改为如下样式:

        .groupeditemspage .groupeditemslist .win-horizontal.win-viewport .win-surface {
            margin-bottom: 60px;
            margin-left: 45px;
            margin-right: 115px;
        }
    

    此 CSS 代码将 margin-bottom 更改为 "50px" 以容纳稍大些的项。

在进行这些更改之后,启动你的应用。它的外观如下所示:

更新的应用

添加图片,更改背景、文本和叠加颜色,之后,你即获得 Contoso 食品运输车登陆页面。

更具创意的大小和模板

用来自定义项模板的方法远不只是此处所显示的方法。 例如,此登陆页面通过使用两个项大小和三个不同的模板来实现平衡外观。

另一个自定义的登陆页面

此登陆页面的基本大小等于最小项的大小。 每个组中第一个项的大小为 2x3 个基本单位,而且有一个用来将标题和描述放在图像下方的模板。 该组中后续项的大小为 1x1 个基本单位,并将标题和描述覆盖到图像上。第三个模板针对的是没有图像的项。

添加 ListView 项动画

在“开始”屏幕中,动态磁贴为用户提供最新的概览图片和文本。 如果适合的话,应用的登陆页面可以使用 WinJS 动画库执行同样的操作。

此示例每隔 4 秒钟(与“开始”屏幕中使用的更新频率相同)使用新图像将登陆页面上的第一项更新一次。 我们使用 WinJS 切换动画(与“开始”用法中的磁贴使用相同的动画)。

  1. 在 Visual Studio 中,创建一个使用 Grid App 模板的新应用。

  2. 在 groupedItems.html 中,修改项模板,使其包括第二个图像,该图像将用于动画。

        <div class="itemtemplate" data-win-control="WinJS.Binding.Template">
            <div class="item">
                <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
                <img class="item-image-new" src="#" data-win-bind="src: backgroundImage; alt: title" />
                <div class="item-overlay">
                    <h4 class="item-title" data-win-bind="textContent: title"></h4>
                    <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
                </div>
            </div>
        </div>
    
  3. 在 groupedItems.css 中,修改 CSS,以便将第二个图像放在原始图像下方。 这允许我们从项的底部开始,将新图像动画处理到所需的位置。 我们需要让这两个项都使用相对位置,以便我们可以在开始进行动画处理之前更改这两个项的位置。 第一个和第三个 CSS 样式已经存在于 groupedItems.css 中,而且只需要进行修改。第二个 CSS 样式是新增样式。

            /* Update this CSS style. */
            .groupeditemspage .groupeditemslist .item .item-image {
                -ms-grid-row-span: 2;
                position:relative;
            }
    
            /* Add this CSS style. */
            .groupeditemspage .groupeditemslist .item .item-image-new {
                -ms-grid-row-span: 2;
                position:relative;
                top: 250px;
            }
    
            /* Update this CSS style. */
            .groupeditemspage .groupeditemslist .item .item-overlay {
                -ms-grid-row: 2;
                -ms-grid-rows: 1fr 21px;
                display: -ms-grid;
                padding: 6px 15px 2px 15px;
                position:relative;
            }
    
  4. 在 groupedItems.js 中,将以下代码添加到 ready 函数中,以便每隔 4 秒触发一次新的项动画。

                setInterval(function () { changeImage() } , 4000);
    
  5. 在 groupedItems.js 中,将以下代码添加到页面定义外部。 第一个变量定义指向 Grid App 模板所使用的不同图像。添加 peekTile 函数以播放适用于 JavaScript 的 Windows 库切换动画。 添加 changeImage 函数,以便在播放动画之前更新图像。 在以下示例中,我们仅针对 ListView 中的第一项播放动画。

        // Define images
        var darkGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY3B0cPoPAANMAcOba1BlAAAAAElFTkSuQmCC";
        var lightGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY7h4+cp/AAhpA3h+ANDKAAAAAElFTkSuQmCC";
        var mediumGray = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXY5g8dcZ/AAY/AsAlWFQ+AAAAAElFTkSuQmCC";
    
        // Play the Peek animation
        function peekTile(tile1, tile2) {
            // Create peek animation
            var peekAnimation = WinJS.UI.Animation.createPeekAnimation([tile1, tile2]);
    
            // Reposition tiles to their desired post-animation position
            tile1.style.top = "-250px";
            tile2.style.top = "0px";
    
            // Execute animation
            peekAnimation.execute();
        }
    
       function changeImage() {
            // Get the two image elements
            var images = document.querySelector(".item-image");
            var imagesNew = document.querySelector(".item-image-new"); 
    
            // Swap out the old image source and choose the new image source
            images.src = imagesNew.src;
            if (images.src == lightGray)
                imagesNew.src = mediumGray;
            else if (images.src == mediumGray)
                imagesNew.src = darkGray;
            else
                imagesNew.src = lightGray;
    
            // Reset the elements for the pre-animation position and trigger the animation
            images.style.top = "0px";
            imagesNew.style.top = "250px";
            peekTile(images, imagesNew);
        };
    

    恭喜你,你已经在 ListView 中添加了自定义的项动画!

相关主题

ListView

快速入门:添加 ListView

设置 ListView 及其项目的样式