Sdílet prostřednictvím


Výkon ListView

Při psaní mobilních aplikací záleží na výkonu. Uživatelé přišli očekávat hladké posouvání a rychlé načítání. Když se vám nepodaří splnit očekávání vašich uživatelů, budou vám náklady na hodnocení v obchodě s aplikacemi nebo v případě obchodní aplikace stát čas a peníze vaší organizace.

Jedná se Xamarin.FormsListView o výkonné zobrazení pro zobrazení dat, ale má určitá omezení. Při používání vlastních buněk může dojít k snížení výkonu posouvání, zejména pokud obsahují hluboko vnořené hierarchie zobrazení nebo používají určitá rozložení, která vyžadují komplexní měření. Naštěstí existují techniky, které můžete použít, abyste se vyhnuli nízkému výkonu.

strategie Ukládání do mezipaměti

Objekty ListView se často používají k zobrazení mnohem více dat, než se vejde na obrazovku. Hudební aplikace může mít například knihovnu skladeb s tisíci položek. Vytvoření položky pro každou položku by ztrácelo cennou paměť a fungovalo špatně. Vytváření a zničení řádků by neustále vyžadovalo vytvoření instance a vyčištění objektů aplikace, což by také fungovalo špatně.

Pro zachování paměti mají nativní ListView ekvivalenty pro každou platformu integrované funkce pro opakované využití řádků. Do paměti se načtou jenom buňky viditelné na obrazovce a obsah se načte do existujících buněk. Tento model brání aplikaci v vytváření instancí tisíců objektů, což šetří čas a paměť.

Xamarin.FormsListView umožňuje opakované použití buněk prostřednictvím výčtuListViewCachingStrategy, který má následující hodnoty:

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

Poznámka:

Univerzální platforma Windows (UPW) ignoruje RetainElement strategii ukládání do mezipaměti, protože ke zlepšení výkonu vždy používá ukládání do mezipaměti. Ve výchozím nastavení se proto chová jako při RecycleElement použití strategie ukládání do mezipaměti.

ZachovatElement

Strategie RetainElement ukládání do mezipaměti určuje, že ListView se pro každou položku v seznamu vygeneruje buňka a že se jedná o výchozí ListView chování. Měla by být použita za následujících okolností:

  • Každá buňka má velký počet vazeb (20-30+).
  • Šablona buňky se často mění.
  • Testování ukazuje, že RecycleElement strategie ukládání do mezipaměti vede ke snížení rychlosti provádění.

Při práci s vlastními buňkami je důležité rozpoznat důsledky RetainElement strategie ukládání do mezipaměti. Každý inicializační kód buňky bude potřeba spustit pro každé vytvoření buňky, což může být několikrát za sekundu. V této situaci se techniky rozložení, které byly na stránce v pořádku, jako je použití více vnořených StackLayout instancí, stanou kritickými body výkonu při jejich nastavení a zničení v reálném čase při posouvání uživatele.

RecycleElement

Strategie RecycleElement ukládání do mezipaměti určuje, že ListView se pokusí minimalizovat nároky na paměť a rychlost spouštění díky recyklaci buněk seznamu. Tento režim nenabízí vždy zlepšení výkonu a testování by se mělo provést, aby bylo možné určit všechna vylepšení. Je to ale upřednostňovaná volba a měla by se použít za následujících okolností:

  • Každá buňka má malý až střední počet vazeb.
  • Každá buňka BindingContext definuje všechna data buněk.
  • Každá buňka je z velké části podobná a šablona buňky se nemění.

Během virtualizace bude mít buňka aktualizovaný kontext vazby, takže pokud aplikace používá tento režim, musí zajistit, aby aktualizace kontextu vazby byly zpracovány odpovídajícím způsobem. Všechna data o buňce musí pocházet z kontextu vazby nebo mohou nastat chyby konzistence. Tento problém lze vyhnout pomocí datové vazby k zobrazení dat buňky. Další možností je nastavit data buňky v OnBindingContextChanged přepsání, nikoli v konstruktoru vlastní buňky, jak je znázorněno v následujícím příkladu kódu:

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;
        }
    }
}

Další informace naleznete v tématu Změny kontextu vazby.

Pokud buňky v iOSu a Androidu používají vlastní renderery, musí zajistit správnou implementaci oznámení o změně vlastnosti. Při opakovaném použití buněk se hodnoty vlastností změní při aktualizaci kontextu vazby na dostupnou buňku s PropertyChanged vyvoláním událostí. Další informace naleznete v tématu Přizpůsobení ViewCell.

RecycleElement s objektem DataTemplateSelector

ListView Pokud se použije DataTemplateSelector k výběru , DataTemplateRecycleElement strategie ukládání do mezipaměti neukládá mezipamětiDataTemplate. DataTemplate Místo toho je vybrána pro každou položku dat v seznamu.

Poznámka:

Strategie RecycleElement ukládání do mezipaměti má předpoklad zavedený ve Xamarin.Forms verzi 2.4, že když DataTemplateSelector se zobrazí výzva k výběru DataTemplate , který musí každý DataTemplate vrátit stejný ViewCell typ. Pokud je například vrácena ListView hodnota typu DataTemplateSelector , která může vrátit buď MyDataTemplateA (kde MyDataTemplateA vrátí ViewCell typ MyViewCellA), nebo MyDataTemplateB (kde MyDataTemplateB vrátí ViewCell typ MyViewCellB), když MyDataTemplateA je vrácena, musí vrátit MyViewCellA nebo se vyvolá výjimka.

RecycleElementAndDataTemplate

Strategie RecycleElementAndDataTemplate ukládání do mezipaměti je založená na RecycleElement strategii ukládání do mezipaměti tím, že navíc zajišťuje, že když ListView se použije DataTemplateSelector k výběru , DataTemplateDataTemplatebudou uloženy v mezipaměti podle typu položky v seznamu. DataTemplateProto jsou vybrány jednou za typ položky místo jednou na instanci položky.

Poznámka:

RecycleElementAndDataTemplate Strategie ukládání do mezipaměti má předpoklad, že DataTemplatevrácené vrácené DataTemplateSelector musí použít DataTemplate konstruktor, který přebírá Type.

Nastavení strategie ukládání do mezipaměti

Hodnota ListViewCachingStrategy výčtu je zadána s přetížením konstruktoru ListView , jak je znázorněno v následujícím příkladu kódu:

var listView = new ListView(ListViewCachingStrategy.RecycleElement);

V JAZYCE XAML nastavte CachingStrategy atribut, jak je znázorněno v následujícím kódu XAML:

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

Tato metoda má stejný účinek jako nastavení argumentu strategie ukládání do mezipaměti v konstruktoru v jazyce C#.

Nastavení strategie ukládání do mezipaměti v podtřídě ListView

Nastavení atributu CachingStrategy z XAML v podtřídě ListView nevyvolá požadované chování, protože neexistuje žádná CachingStrategy vlastnost ListView. Kromě toho, pokud je povolen XAMLC, bude vytvořena následující chybová zpráva: Žádná vlastnost, bindable vlastnost nebo událost nalezena pro 'Ukládání do mezipaměti Strategy'

Řešením tohoto problému je zadat konstruktor v podtřídě ListView , který přijímá ListViewCachingStrategy parametr a předává ho do základní třídy:

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

Potom lze hodnotu výčtu ListViewCachingStrategy zadat z XAML pomocí x:Arguments syntaxe:

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

Návrhy výkonu ListView

Existuje mnoho technik pro zlepšení výkonu ListView. Následující návrhy můžou zlepšit výkon zobrazení ListView.

  • ItemsSource Vytvořte vazbu vlastnosti na IList<T> kolekci místo IEnumerable<T> kolekce, protože IEnumerable<T> kolekce nepodporují náhodný přístup.
  • Místo toho používejte předdefinované buňky (například TextCell / SwitchCell ) místo ViewCell toho, abyste je mohli použít.
  • Používejte méně prvků. Zvažte například použití jednoho FormattedString popisku místo více popisků.
  • ListView Nahraďte ho TableView při zobrazení nehomogenních dat – to znamená dat různých typů.
  • Omezte použití Cell.ForceUpdateSize metody. Pokud dojde k nadměrnému výkonu, sníží se výkon.
  • V Androidu se vyhněte nastavení ListViewviditelnosti nebo barvy oddělovače řádků po vytvoření instance, protože výsledkem je velký výkon.
  • Vyhněte se změně rozložení buňky na základě .BindingContext Změna rozložení způsobuje velké náklady na měření a inicializaci.
  • Vyhněte se hluboko vnořeným hierarchií rozložením. Použijte AbsoluteLayout nebo Grid pomozte snížit vnoření.
  • Vyhněte se jiným než LayoutOptionsFill (Fill je nejlevnější pro výpočty).
  • Vyhněte se umístění ListView uvnitř z ScrollView následujících důvodů:
    • Implementuje ListView vlastní posouvání.
    • Nebudou ListView přijímat žádná gesta, protože bude zpracována nadřazeným ScrollViewobjektem .
    • Může ListView prezentovat přizpůsobené záhlaví a zápatí, které se posune s prvky seznamu, a potenciálně nabízí funkce, pro které ScrollView byl použit. Další informace najdete v tématu Záhlaví a zápatí.
  • Pokud potřebujete konkrétní komplexní návrh zobrazený v buňkách, zvažte vlastní renderer.

AbsoluteLayout má potenciál provádět rozložení bez jediného volání míry, což z něj dělá vysoce výkonné. Pokud AbsoluteLayout nelze použít, zvažte RelativeLayout. Pokud používáte RelativeLayout, předávání omezení přímo bude výrazně rychlejší než použití rozhraní API pro výrazy. Tato metoda je rychlejší, protože rozhraní API výrazů používá JIT a v iOSu je potřeba strom interpretovat, což je pomalejší. Rozhraní API výrazu je vhodné pro rozložení stránek, kde se vyžaduje pouze při počátečním rozložení a otočení, ale v místě, kde ListViewse při posouvání spouští neustále, snižuje výkon.

Vytvoření vlastního rendereru ListView pro buňky nebo jeho buněk je jedním z přístupů ke snížení vlivu výpočtů rozložení při posouvání výkonu. Další informace naleznete v tématu Přizpůsobení objektu ListView a přizpůsobení objektu ViewCell.