Поделиться через


Производительность ListView

При написании мобильных приложений производительность имеет значение. Пользователи ожидали плавной прокрутки и быстрой загрузки. Не отвечая ожиданиям пользователей, вы будете стоить оценки в магазине приложений или в случае бизнес-приложения, стоить вашей организации время и деньги.

Это Xamarin.FormsListView мощное представление для отображения данных, но оно имеет некоторые ограничения. Производительность прокрутки может страдать при использовании пользовательских ячеек, особенно если они содержат иерархии глубоко вложенных представлений или используют определенные макеты, требующие сложного измерения. К счастью, есть методы, которые можно использовать, чтобы избежать плохой производительности.

Стратегия кэширования

ListViews часто используются для отображения гораздо большего количества данных, чем соответствует экрану. Например, у музыкального приложения может быть библиотека песен с тысячами записей. Создание элемента для каждой записи будет тратить ценную память и плохо работать. Постоянно создавая и уничтожая строки, приложение будет постоянно создавать экземпляры и очищать объекты, которые также будут работать плохо.

Для экономии памяти собственные ListView эквиваленты для каждой платформы имеют встроенные функции для повторного использования строк. Только ячейки, видимые на экране, загружаются в память, а содержимое загружается в существующие ячейки. Этот шаблон не позволяет приложению создавать экземпляры тысяч объектов, сохранять время и память.

Xamarin.FormsListView разрешает повторное использование ячеек через ListViewCachingStrategy перечисление, которое имеет следующие значения:

public enum ListViewCachingStrategy
{
    RetainElement,   // the default value
    RecycleElement,
    RecycleElementAndDataTemplate
}

Примечание.

Универсальная платформа Windows (UWP) игнорирует RetainElement стратегию кэширования, так как она всегда использует кэширование для повышения производительности. Поэтому по умолчанию она ведет себя так, как если RecycleElement бы применяется стратегия кэширования.

СохранитьElement

Стратегия RetainElement кэширования указывает, что ListView будет создавать ячейку для каждого элемента в списке и является поведением по умолчанию ListView . Его следует использовать в следующих обстоятельствах:

  • Каждая ячейка имеет большое количество привязок (20–30+).
  • Шаблон ячейки часто изменяется.
  • Тестирование показывает, что RecycleElement стратегия кэширования приводит к снижению скорости выполнения.

Важно распознать последствия RetainElement стратегии кэширования при работе с пользовательскими ячейками. Любой код инициализации ячеек должен выполняться для каждого создания ячейки, что может быть несколько раз в секунду. В этом случае методы макета, которые были тонкими на странице, как использование нескольких вложенных StackLayout экземпляров, становятся узкими местами производительности при настройке и уничтожении в режиме реального времени при прокрутке пользователя.

RecycleElement

Стратегия RecycleElement кэширования указывает, что ListView попытка свести к минимуму объем памяти и скорость выполнения путем перезапуска ячеек списка. Этот режим не всегда предлагает улучшение производительности и тестирование должно выполняться для определения каких-либо улучшений. Тем не менее, это предпочтительный выбор, и его следует использовать в следующих обстоятельствах:

  • Каждая ячейка имеет небольшое или умеренное количество привязок.
  • Каждая ячейка BindingContext определяет все данные ячейки.
  • Каждая ячейка в значительной степени аналогична шаблону ячейки.

Во время виртуализации ячейка будет обновлять контекст привязки и поэтому, если приложение использует этот режим, оно должно убедиться, что обновления контекста привязки обрабатываются соответствующим образом. Все данные о ячейке должны поступать из контекста привязки или ошибок согласованности. Эту проблему можно избежать с помощью привязки данных для отображения данных ячейки. Кроме того, данные ячеек должны быть заданы в переопределении, а не в OnBindingContextChanged конструкторе пользовательской ячейки, как показано в следующем примере кода:

public class CustomCell : ViewCell
{
    Image image = null;
    
    public CustomCell ()
    {
        image = new Image();
        View = image;
    }
    
    protected override void OnBindingContextChanged ()
    {
        base.OnBindingContextChanged ();
        
        var item = BindingContext as ImageItem;
        if (item != null) {
            image.Source = item.ImageUrl;
        }
    }
}

Дополнительные сведения см. в разделе "Изменения контекста привязки".

В iOS и Android, если ячейки используют пользовательские отрисовщики, они должны убедиться, что уведомление об изменении свойств реализовано правильно. Когда ячейки повторно используются, значения свойств изменяются при обновлении контекста привязки до доступной ячейки, при PropertyChanged этом возникают события. Дополнительные сведения см. в разделе "Настройка ViewCell".

RecycleElement с dataTemplateSelector

Если используется для DataTemplateSelectorListView выбораDataTemplate, RecycleElement стратегия кэширования не кэширует DataTemplates. Вместо этого выбирается для DataTemplate каждого элемента данных в списке.

Примечание.

Стратегия RecycleElement кэширования имеет необходимые требования, представленные в Xamarin.Forms версии 2.4, что при DataTemplateSelector запросе выбрать DataTemplate тот же тип, каждый из которых DataTemplate должен возвращать один и тот же ViewCell тип. Например, если задано ListView значение, DataTemplateSelector которое может возвращать MyDataTemplateA либо (где MyDataTemplateA возвращает типMyViewCellA), либо MyDataTemplateB (где MyDataTemplateB возвращает ViewCellMyViewCellBViewCell тип), когда MyDataTemplateA возвращается он должен возвращать MyViewCellA или возникает исключение.

RecycleElementAndDataTemplate

Стратегия RecycleElementAndDataTemplate кэширования основывается на RecycleElement стратегии кэширования, дополнительно гарантируя, что при ListView использовании DataTemplateSelector выбранного DataTemplateDataTemplateэлемента кэшируются по типу элемента в списке. Таким образом, DataTemplateзначения s выбираются один раз на тип элемента, а не один раз на экземпляр элемента.

Примечание.

Стратегия RecycleElementAndDataTemplate кэширования имеет предварительные требования, которые DataTemplateвозвращаются DataTemplateSelectorDataTemplate конструктором, который принимает Type.

Настройка стратегии кэширования

Значение ListViewCachingStrategy перечисления указывается с перегрузкой ListView конструктора, как показано в следующем примере кода:

var listView = new ListView(ListViewCachingStrategy.RecycleElement);

В XAML задайте CachingStrategy атрибут, как показано в коде XAML ниже:

<ListView CachingStrategy="RecycleElement">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
              ...
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Этот метод имеет тот же эффект, что и установка аргумента стратегии кэширования в конструкторе в C#.

Настройка стратегии кэширования в подклассном ListView

CachingStrategy Задание атрибута из XAML в подклассе ListView не приведет к возникновению требуемого поведения, так как в ней нет CachingStrategy свойстваListView. Кроме того, если XAMLC включен, будет создано следующее сообщение об ошибке: нет свойства, привязываемого свойства или события для CachingStrategy.

Решение этой проблемы заключается в указании конструктора подкласса ListView , который принимает ListViewCachingStrategy параметр и передает его в базовый класс:

public class CustomListView : ListView
{
    public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
    {
    }
    ...
}

ListViewCachingStrategy Затем значение перечисления можно указать из XAML с помощью синтаксисаx:Arguments:

<local:CustomListView>
    <x:Arguments>
        <ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
    </x:Arguments>
</local:CustomListView>

Предложения по производительности ListView

Существует множество методов повышения производительности ListView. Следующие предложения могут повысить производительность ListView.

  • Привязывайте ItemsSource свойство к IList<T> коллекции вместо IEnumerable<T> коллекции, так как IEnumerable<T> коллекции не поддерживают случайный доступ.
  • Используйте встроенные ячейки (например TextCell / SwitchCell ), а не всякий ViewCell раз, когда вы можете.
  • Используйте меньше элементов. Например, рекомендуется использовать одну FormattedString метку вместо нескольких меток.
  • Замените его ListViewTableView на отображение неоднородных данных, то есть данных разных типов.
  • Ограничение использования Cell.ForceUpdateSize метода. Если превышено, это приведет к снижению производительности.
  • В Android не устанавливайте ListViewвидимость или цвет разделителя строк после создания экземпляра, так как это приводит к большому штрафу за производительность.
  • Избегайте изменения макета ячейки на BindingContextоснове . Изменение макета вызывает большие затраты на измерение и инициализацию.
  • Избегайте глубоко вложенных иерархий макета. Использование AbsoluteLayout или Grid уменьшение вложения.
  • Избегайте конкретных, LayoutOptions кроме Fill (Fill является самым дешевым для вычислений).
  • Избегайте размещения ListView внутри по ScrollView следующим причинам:
    • Реализует ListView собственную прокрутку.
    • Не ListView будет получать никаких жестов, так как они будут обрабатываться родительским ScrollViewэлементом.
    • Он ListView может представить настраиваемый верхний и нижний колонтитул, который прокручивается с элементами списка, потенциально предлагая функциональные возможности, которые ScrollView использовались для. Дополнительные сведения см. в разделе "Верхние и нижние колонтитулы".
  • Рассмотрите пользовательский отрисовщик, если вам нужен конкретный сложный дизайн, представленный в ячейках.

AbsoluteLayout имеет потенциал для выполнения макетов без вызова одной меры, что делает его высокопроизводительным. Если AbsoluteLayout не удается использовать, рассмотрите вопрос RelativeLayout. При использовании RelativeLayoutпередача ограничений напрямую будет значительно быстрее, чем с помощью API выражений. Этот метод быстрее, так как API выражений использует JIT, и в iOS дерево должно быть интерпретировано медленнее. API выражений подходит для макетов страниц, где он требуется только для начального макета и поворота, но в ListViewтом месте, где он выполняется постоянно во время прокрутки, он повредит производительность.

Создание пользовательского отрисовщика для ListView ячейки является одним из способов уменьшения эффекта вычислений макета на производительность прокрутки. Дополнительные сведения см. в разделе "Настройка ListView " и настройка ViewCell.