Коллекции и элементы управления для вывода коллекций
Особенности и стилизация ListView
Рассмотрев сведения об источниках данных, шаблонах, вместе с множеством примеров использования ListView, мы можем сейчас рассмотреть дополнительные особенности этого элемента управления, такие, как макеты, стилизация и размещение в нескольких объединенных ячейках для элементов большого размера, в составе которых имеется несколько подэлементов. Оптимизации производительности посвящен последний раздел данной лекции. Во-первых, однако, позвольте мне ответить на один очень важный вопрос.
Когда ListView - это неправильный выбор?
ListView - это один из богатейших по возможностей элементов управления во всей Windows. Он очень мощный, гибкий, и, как мы уже знаем, весьма глубокий и сложный. Но по всем этим причинам, иногда, он является не лучшим выбором! В зависимости от дизайна, может быть проще использовать обычный HTML/CSS макет
Концептуально ListView определяется путём задания взаимоотношений трех частей: источника данных, шаблонов и макета. То есть, объекты из источника данных, которые могут быть сгруппированы, отсортированы и отфильтрованы, выводятся с использованеим шаблонов и организуются с помощью макета (обычно - с помощью групп и заголовков групп). В подобном определении, ListView предназначен для того, чтобы помочь в визуализации коллекций похожих и/или взаимосвязанных объектов, где группировка тоже подразумевает взаимоотношения определенного рода.
Имея это в виду, можно признать следующие факторы убедительно свидетельствующими о том, что ListView - это хороший выбор для вывода некоторой коллекции:
- Коллекция может содержать различное чисто объектов для вывода, возможно, очень большое количество, которых больше, чем приложение, запущенное на большом экране, способно отобразить в одном окне.
- Имеет значение организация и реорганизация элементов в различных группах.
- Заголовки групп поомогают уточнить общие свойства элементов в группах, и их можно использовать для навигации к странице детальной информации о группе.
- Имеет значение сортировка или/и фильтрация элементов в соответствии с различными условиями.
- Различная группировка элементов и информации об этих группах предлагает способы работы, в которых опыт взаимодействия пользователя и программы способно улучшить семантическое масштабирование.
- Группы имеют некоторые общие черты, в том смысле, что каждая из них связана с похожими объектами. Различные имена мест, например, сходны; лента новостей, список друзей и календарь праздников не схожи.
- Элементы поддерживают индивидуальное или групповое выделение, таким образом, что команды на панели приложения позволяют выполнять с ними какие-то действия.
С другой стороны, противоположные факторы говорят о том, что ListView не является правильным выбором:
- Коллекция содержит ограниченное или фиксированное количество элементов, или это совсем не коллекция взаимосвязанных элементов.
- Неважна возможность реорганизации группировки объектов, фильтрация или сортировка элементов.
- Вам не нужны заголовки групп.
- Вы не нуждаетесь в применении семантического масштабирования.
- Группы весьма различны - то есть, группам не имеет смысла находиться бок о бок при отсутствии заголовков.
Позвольте мне пояснить, что я не говорю о дизайне - ваш дизайнер может передать вам любой макет, который он хочет, и, как разработчик, именно вы должны решить, как реализовать его! Я говорю о том, как вы выбираете подход для подобной реализации, с использованием ли элементов управления наподобие ListView, или с применением обычного HTML/CSS макета.
Я говорю об этом, так как в работе с разработчиками, которые создают самые первые приложения для Магазина Windows, мы часто видим, как они пытаются использовать ListView в ситуации, где он просто не нужен. Домашняя страница приложения, например, может содержать комбинацию из лент новостей, списков друзей и календаря. Страница детальной информации об элементе может включать в себя изображение, текстовое описание и медиа-библиотеку. В обоих случаях, страница содержит ограниченное количество разделов и разделы содержат очень разное содержимое, о котором можно сказать, что оно не имеет межгрупповых связей. Поэтому, использование ListView более сложно, чем простое использование одного div, поддерживающего прокрутку с CSS-сеткой, в которой вы можете разместить любые разделы, которые вам нужны.
Внутри данных разделов, конечно, вы можете использовать элементы управления ListView для отображения коллекций элементов, но если говорить обо всей странице, то обычный макет, основанный на div - это всё, что вам нужно. Я показал подобный выбор на Рис. 5.7, используя изображение из материала "Проектирование навигации для приложений Магазина Windows" (http://msdn.microsoft.com/library/windows/apps/hh761500), так как вы, возможно, получите похожие изображения от вашего дизайнера. За исключением навигационных стрелок, хаб приложения и страницы детальной информации обычно используют div в качестве корневого элемента, в то время как страница раздела обычно представляет собой ListView. Внутри корневого узла приложения и страниц детальной информации могут быть элементы управления ListView, но там, где нужно отобразить фиксированное содержимое (вроде отдельного элемента), лучше подойдёт div.
увеличить изображение
Рис. 5.7. Разбиение типичного дизайна Хаб-Раздел-Сведения (Hub-Section-Details), спроектированного с применением элементов div и элементов управления ListView
Корневой раздел приложения (хаб): страница - это div; разделы в сетке - это либо div'ы, либо элементы управления ListView; если первый раздел содержит различные элементы, ей следует содержать ListView (Hub page: page is a div; sections in a grid are either divs or ListViews; if the first section has variable items, it could be a ListView).
Элемент div с макетом (div with layout)
Страница раздела: тело страницы (исключая заголовок) - это один ListView (Section page: the body of the page (excluding a page header) is a single ListView)
Страница детальной информации (сведений): страница - это div; разделы в сетке - либо элементы div, либо - элементы управления ListView (Detail page: page is a div; sections in a grid are either divs or ListViews)
Подсказывает о том, что вы идете по неверному пути, кстати, тот факт, что вы пытаетесь скомбинировать несколько коллекций несвязанных данных в единый источник данных, привязывая этот источник к ListView и реализуя функцию рендеринга для того, чтобы снова разделить эти данные, чтобы всё вывелось верно. Всей этой дополнительной работы можно избежать, просто используя макет HTML/CSS.
Для того, чтобы узнать больше о проектировании ListView, обратитесь к материалу: "Руководство и контрольный список для элементов управления ListView" (http://msdn.microsoft.com/library/windows/apps/hh465465.aspx).
Параметры, выделения и методы отдельного элемента
В предыдущем разделе мы уже видели некоторые параметры, которые вы можете использовать при создании ListView, параметры, которые связаны со свойствами элемента управления и доступны, кроме того, из JavaScript. Посмотрим на полный список свойств, методов и событий, которые я организовал в несколько групп, в конце концов, эти свойства и методы превратились во что-то вроде коллекции! Подробности о них вы можете найти на странице WinJS.UI.ListView (http://msdn.microsoft.com/library/windows/apps/br211837.aspx), самое важное сейчас - понять как взаимосвязаны члены данных групп:
- Адресация элементов. Свойство currentItem позволяет получить или задать элемент, имеющий фокус, а методы elementFromIndex и indexOfElement позволяют установить взаимосвязь между элементами DOM и их индексами. Последний может быть полезен, если у вас есть другие элементы управления в шаблоне элемента, и вам нужно определить окружающий элемент в обработчике события.
- Видимость элементов. Свойства indexOfFirstVisible и indexOfLastVisible позволяют узнать индексы видимых элементов, или они могут быть использованы для прокрутки ListView к заданному элементу. Метод ensureVisible приводит к отображению заданного элемента, если он загружен. Здесь имеется свойство scrollPosition, которое содержит дистанцию в пикселях между первым элементом в списке и текущей отображаемой областью. Последством этого свойства вы можете задать позицию прокрутки ListView, это работает только в том случае, если значение loadingState (смотрите группу "Состояние загрузки" ниже") установлено в ready, в противном случае ListView может еще не знать своих истинных размеров. Рекомендовано, вместо этого, использовать ensureVisible или indexOfFirstVisible для управления позицией прокрутки.
- Активизация элементов. Событие itemInvoked, как мы уже видели, вызывается, когда пользователь касается элемента, если только свойство tapBehavior не установлено в none - в подобном случае активации не происходит. Другое значение tapBehavior из перечисления WinJS.UI.tapBehavior (http://msdn.microsoft.com/library/windows/apps/hh701303.aspx) всегда приводит к вызову данного события, но определяет, как касание влияет на выделение. Заметьте, что вы можете переопределить поведение при выделении для каждого элемента в отдельности, используя событие selectionchanging и подавляя анимацию, если это нужно. Смотрите врезку "Поведение при щелчке и прикосновении" ниже.
- Выделение элементов. Свойство selectionMode содержит значение из перечисления WinJS.UI.selectionMode (http://msdn.microsoft.com/library/windows/apps/br229687.aspx), показывающее режим выделения - одиночный, множественный или запрещающее выделение. Во всех случаях свойство selection содержит объект ListViewItems (http://msdn.microsoft.com/library/windows/apps/br211809.aspx), методы которого позволяют вам получать список выделенных элементов и управлять ими (например, настраивать выделенные элементы, используя их метод set). Изменения в выделении вызывают события selectionchanging и selectionchanged. В случае с selectionchanging, его свойство args.detail.newSelection содержит вновь выделенные элементы. Больше об этом можно узнать в примере "Настройка интерактивного поведения HTML ListView" (http://code.msdn.microsoft.com/windowsapps/ListView-selection-detail-95e06ade).
- Прокрутка. Свойство swipeBehavior, связанное с выделением элемента, содержит значение из перечисления WinJS.UI.SwipeBehavior (http://msdn.microsoft.com/library/windows/apps/hh701287.aspx). "Прокрутка" (swiping) или "скольжение по диагонали" (cross-slide) это сенсорный жест на элементе для его выделения, когда жест производится перпендикулярно направлению прокрутки списка. Если данное свойство установлено в none, прокрутка не воздействует на элемент и жест маршрутизируется к родительским элементам, позволяя осуществлять прокрутку вертикально ориентированного ListView или страницы, на которой он расположен. Если это свойство установлено в select, жест обрабатывается элеменом и приводит к его выделению.
- Источники данных и шаблоны. Мы уже видели свойства groupDataSource, groupHeaderTemplate, itemDataSource, и itemTemplate. Два связанных свойства - это resetGroupHeader и resetItem, которые содержат функции, которые ListView может вызывать при повторном использовании элементов. Это разъясняется в разделе "Функции шаблонов (Часть 2)"
- Макет. Как мы уже видели, свойство layout (и объект) описывают организацию элементов в ListView, о чём больше сказано в разделе "Макеты и объединение ячеек" ниже. Мы, кроме того, рассматривали функцию forceLayout, которая используется, когда из ListView удаляется стиль display: none и элемент управления нуждается в повторной визуализации.
- Поведение при загрузке. Как будет обяснено в разделе "Оптимизация производительности ListView" ниже, эта группа определяет, как ListView загружает страницы элементов (то, почему ensureVisible не всегда работает, если страница не загружена). Когда свойство loadingBehavior установлено в "randomaccess" (по умолчанию), полоса прокрутки ListView отражает общее число элементов только на пяти полных страницах элементов (максимум - 1000), хранящихся в памяти в любое время, когда пользователь перемещается по содержимому. (Пять страниц - это текущая страница и по две буферизованных страницы позади текущей и перед ней). Другое значение, "incremental", подразумевает изначальную загрузку некоторого количества страниц и затем, загрузку дополнительных страниц, когда пользователь прокрутит список до конца (сохраняя, после этого, все страницы в памяти). Инкрементная загрузка работает со свойствами automaticallyLoadPages, pagesToLoad, и pagesToLoadThreshold, вместе с методом loadMorePages, как мы увидим далее.
- Состояние загрузки. Свойство только для чтения loadingState содержит либо "itemsLoading" (список запрашивает элементы и заголовки из источника данных), либо "viewportLoaded" (все элементы и заголовки, которые видимы, были загружены), либо "itemsLoaded" (все оставшиеся невидимые буферизованные элементы загружены), или "complete" (все элементы загружены, содержимое в шаблонах выведено и анимация завершена). Когда это свойство изменяется, что обычно происходит, когда ListView нуждается в обновлении макета при прокрутке, вызывается событие loadingStateChange.
- Разное. Стандартные методы DOM addEventListener, removeEventListener, и dispatchEvent служат для обработки и вызова событий. Они могут быть использованы с любыми событиями, которые поддерживает ListView, в том числе contentanimating, которое возникает при исполнении элементом управления анимации при появлении или смене элементов, что позволяет вам либо предотвращать, либо задерживать подобные анимации. Свойство zoomableView содержит реализацию IZoomableView, которая нужна для контекстного масштабирования (приложения никогда не манипулируют данным свойством).
Врезка: Поведение при щелчке и прикосновении
Когда вы прикасаетесь к элементу в ListView или щёлкаете по нему мышью, при том, что свойство tapBehavior установлено во что-то кроме none, происходит небольшая анимация, путём примерно 97% масштабирования для подтверждения данного действия. Если в списке есть некоторые элементы, которые не могут быть активированы (такие, как некоторые группы или те, которые вы показываете как деактивированные, так как данные, лежащие в их основе, пока не доступны), они продолжают показывать эту анимацию, так как установка tapBehavior применяется к элементу управления в целом. Для того чтобы отключить анимацию для любого конкретного объекта, вы можете добавить класс win-interactive к его элементу внутри функции рендеринга, что является способом указания на то, что объект самостоятельно обрабатывает события щелчка или прикосновения, даже если не делает ничего, кроме их приёма. Если позже элемент можно будет активировать, вы можете, конечно, убрать этот класс.
Если вам нужно предотвратить выделение элемента, добавьте обработчик для события ListView selectionchanging и вызовите его метод args.detail.preventtapBehavior. Это работает для всех методов выделения, включая сенсорный жест прокрутки, щелчок мыши и нажатие на клавишу клавиатуры Enter.
Стилизация
После того, как мы коснулись этого в "Элементы управления, их стилизация и привязка данных" и в последнем разделе, посвященном ListView, стилизацию лучше рассматривать на иллюстрациях, как на Рис. 5.8, где я применил некоторые яркие CSS-стили к некоторым из стилей win-* так, чтобы они выделялись. Я рекомендую вам взглянуть на материал "Стилизация ListView и его элементов" (http://msdn.microsoft.com/library/windows/apps/hh850406.aspx) в документации, где детализированы некоторые дополнительные стили, не показанные здесь
Весь элемент управления (entire control)
Зона, где не осуществляется прокрутка (non-scrolling area)
Зона вокруг элемента, на самом деле, лишь для стилизации полей и прозрачности (the area around an item really only to style margin and transparency)
Зона внутри элемента (the area inside an item)
Зона, где осуществляется прокрутка (the scrollable area)
Некоторые замечания о стилизации
- Помните, что в этом деле Blend - ваш лучший друг!
- Как и в случае со стилизацией FlipView, класс наподобие win-listview наиболее полезен для стилей полей и отбивок, в то время как свойства, вроде цвета фона, на самом деле, не отображаются (в отличие от win-viewport и win-surface).
- Фон, не поддерживающий прокрутку, задаваемый win-viewport, используется редко, возможно, из-за неподвижного фонового изображения. Стиль win-surface позволяет настроить прокручиваемую фоновую область.
- win-container существует, преимущественно, ради двух целей. Первая - это создавать свободное пространство между элементами, используя стили margin, и второй - переназначать фоновый цвет, назначенный по умолчанию, часто делая фон прозрачным. Таким образом, фон, задаваемый win-surface или win-viewport виден через него. Обратите внимание, что если вы устанавливаете стиль padding вместо margin, вы создаёте области, по которым пользователь будет перемещаться как по элементам, которые и активируются как элементы. Это не очень хорошо. Поэтому всегда используйте margin для создания свободного пространства между элементами.
- Хотя win-item перечислен как стиль, его использовать не рекомендуется, он может быть удалён в будущем: просто напрямую стилизуйте шаблон элемента.
- Документация указывает на то, что стили вроде win-container и win-surface могут быть использованы несколькими элементами управления WinJS. (FlipView использует несколько таких). Если вы хотите переопределить стили для ListView, убедитесь в том, что их область видимости соответствует другим классам, наподобие .win-listview или конкретным id или классам элемента управления.
- Высота ListView по умолчанию равна 400 пикселям, и элемент управления не подгоняет свой размер автоматически под размер содержимого. Вы практически всегда будете нуждаться в переопределении данного стиля в CSS или устновки его в JavaScript, если вы знаете, какое пространство должен занимать ListView, как показано в "Макет" , "Макет".
- Стили, не показанные на рисунке, но описанные в материале "Стилизация ListView и его элементов" (http://msdn.microsoft.com/library/windows/apps/hh850406.aspx), включают win-focusedoutline, win-selection, win-selected, win-selectionborder, win-selectionbackground, и win-selectionhint. Кроме того, имеется класс win-selectionstylefilled, который вы можете добавить к элементу для использования заполненного (filled) стиля выделения вместо стиля с границей (bordered), который применяется по умолчанию, как показано здесь:
Заставки
Есть еще один визуальный элемент ListView, который похож на стилизацию, но стилизация на него не воздействует. Его называют заставкой (backdrop), это эффект, который включен по умолчанию при использовании gridLayout. На аппаратном обеспечении невысокой производительности, особенно на мобильных устройствах, быстрая прокрутка содержимого ListView может легко опередить возможности элемента управления по загрузке и выводу элементов. Для того, чтобы пользователь видел результат своих действий, gridLayout показывает обычные заставки для элементов, основанные на размерах элементов по умолчанию и прокручивает их до момента вывода элементов. Как мы увидим в следующем разделе, вы можете выключить эту возможность с помощью свойства gridLayout disableBackgdrop и переопределить его серый цвет, применяемый по умолчанию, с помощью свойства backdropColor.