Udostępnij za pośrednictwem


Wydajność elementu ListView

Podczas pisania aplikacji mobilnych wydajność ma znaczenie. Użytkownicy spodziewają się bezproblemowego przewijania i szybkiego ładowania. Brak spełnienia oczekiwań użytkowników spowoduje koszt klasyfikacji w sklepie z aplikacjami lub w przypadku aplikacji biznesowej kosztem czasu organizacji i pieniędzy.

Jest Xamarin.FormsListView to zaawansowany widok wyświetlania danych, ale ma pewne ograniczenia. Wydajność przewijania może mieć miejsce w przypadku używania komórek niestandardowych, zwłaszcza gdy zawierają głęboko zagnieżdżone hierarchie widoków lub używają niektórych układów wymagających złożonego pomiaru. Na szczęście istnieją techniki, których można użyć, aby uniknąć niskiej wydajności.

Strategia buforowania

Kontrolki ListView są często używane do wyświetlania znacznie większej ilości danych niż mieści się na ekranie. Na przykład aplikacja muzyczna może mieć bibliotekę piosenek z tysiącami wpisów. Utworzenie elementu dla każdego wpisu spowoduje marnowanie cennej pamięci i słabe działanie. Tworzenie i niszczenie wierszy stale wymagałoby, aby aplikacja tworzyła wystąpienia obiektów i czyściła je stale, co również działałoby źle.

Aby zaoszczędzić pamięć, natywne ListView odpowiedniki dla każdej platformy mają wbudowane funkcje ponownego użycia wierszy. Tylko komórki widoczne na ekranie są ładowane w pamięci, a zawartość jest ładowana do istniejących komórek. Ten wzorzec uniemożliwia aplikacji utworzenie wystąpienia tysięcy obiektów, oszczędność czasu i pamięci.

Xamarin.FormsListView zezwala na ponowne użycie komórek za pomocą ListViewCachingStrategy wyliczenia, które ma następujące wartości:

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

Uwaga

Platforma uniwersalna systemu Windows (UWP) ignoruje strategię RetainElement buforowania, ponieważ zawsze używa buforowania w celu zwiększenia wydajności. W związku z tym domyślnie zachowuje się tak, jakby zastosowano strategię RecycleElement buforowania.

Zachowaj element

Strategia RetainElement buforowania określa, że ListView wygeneruje komórkę dla każdego elementu na liście i jest zachowaniem domyślnym ListView . Należy go użyć w następujących okolicznościach:

  • Każda komórka ma dużą liczbę powiązań (20–30+).
  • Szablon komórki często się zmienia.
  • Testowanie pokazuje, że RecycleElement strategia buforowania powoduje zmniejszenie szybkości wykonywania.

Ważne jest, aby rozpoznać konsekwencje RetainElement strategii buforowania podczas pracy z komórkami niestandardowymi. Każdy kod inicjowania komórek musi zostać uruchomiony dla każdego utworzenia komórki, co może być wielokrotnie na sekundę. W takiej sytuacji techniki układu, które były w porządku na stronie, takie jak używanie wielu zagnieżdżonych StackLayout wystąpień, stają się wąskimi gardłami wydajności podczas konfigurowania i niszczenia w czasie rzeczywistym podczas przewijania użytkownika.

RecycleElement

Strategia RecycleElement buforowania określa, że ListView próba zminimalizowania zużycia pamięci i szybkości wykonywania przez odtworzenie komórek listy. Ten tryb nie zawsze oferuje poprawę wydajności, a testowanie należy wykonać w celu ustalenia wszelkich ulepszeń. Jest to jednak preferowany wybór i powinien być używany w następujących okolicznościach:

  • Każda komórka ma niewielką do umiarkowaną liczbę powiązań.
  • Każda BindingContext komórka definiuje wszystkie dane komórek.
  • Każda komórka jest w dużej mierze podobna, a szablon komórki jest niezmienny.

Podczas wirtualizacji komórka będzie miała zaktualizowany kontekst powiązania, a więc jeśli aplikacja korzysta z tego trybu, musi upewnić się, że aktualizacje kontekstu powiązania są odpowiednio obsługiwane. Mogą wystąpić wszystkie dane dotyczące komórki z kontekstu powiązania lub błędy spójności. Ten problem można uniknąć, używając powiązania danych do wyświetlania danych komórek. Alternatywnie dane komórek powinny być ustawiane w OnBindingContextChanged przesłonięć, a nie w konstruktorze komórki niestandardowej, jak pokazano w poniższym przykładzie kodu:

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

Aby uzyskać więcej informacji, zobacz Binding Context Changes (Zmiany kontekstu powiązania).

W systemach iOS i Android, jeśli komórki używają niestandardowych modułów renderujących, muszą upewnić się, że powiadomienie o zmianie właściwości jest poprawnie zaimplementowane. Gdy komórki zostaną ponownie użyte, ich wartości właściwości zmienią się, gdy kontekst powiązania zostanie zaktualizowany do dostępnej komórki z wywoływanymi zdarzeniami PropertyChanged . Aby uzyskać więcej informacji, zobacz Dostosowywanie elementu ViewCell.

RecycleElement za pomocą elementu DataTemplateSelector

Jeśli element ListView używa DataTemplateSelector elementu do wybrania DataTemplateelementu , RecycleElement strategia buforowania nie buforuje DataTemplatewartości s. Zamiast tego element DataTemplate jest wybierany dla każdego elementu danych na liście.

Uwaga

Strategia RecycleElement buforowania ma wymagania wstępne wprowadzone w Xamarin.Forms wersji 2.4, że po wyświetleniu monitu DataTemplateSelector DataTemplate o wybranie elementu, każdy DataTemplate musi zwrócić ten sam ViewCell typ. Na przykład w przypadku ListView elementu z wartością DataTemplateSelector , która może zwrócić wartość MyDataTemplateA (gdzie MyDataTemplateA zwraca ViewCell typ MyViewCellA), lub MyDataTemplateB (gdzie MyDataTemplateB zwraca ViewCell MyViewCellBtyp ), gdy MyDataTemplateA jest zwracana wartość musi zostać zwrócona MyViewCellA lub zostanie zgłoszony wyjątek.

RecycleElementAndDataTemplate

Strategia RecycleElementAndDataTemplate buforowania opiera się na RecycleElement strategii buforowania, zapewniając dodatkowo, że w przypadku ListView DataTemplateSelector wybrania DataTemplateelementu , DataTemplates są buforowane przez typ elementu na liście. DataTemplateW związku z tym s są wybierane raz na typ elementu, a nie raz na wystąpienie elementu.

Uwaga

Strategia RecycleElementAndDataTemplate buforowania ma warunek wstępny, który DataTemplatezwracany przez DataTemplateSelector element musi używać konstruktora DataTemplate , który przyjmuje Typeelement .

Ustawianie strategii buforowania

Wartość ListViewCachingStrategy wyliczenia jest określona z przeciążeniem konstruktora ListView , jak pokazano w poniższym przykładzie kodu:

var listView = new ListView(ListViewCachingStrategy.RecycleElement);

W języku CachingStrategy XAML ustaw atrybut, jak pokazano w poniższym kodzie XAML:

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

Ta metoda ma taki sam efekt, jak ustawienie argumentu strategii buforowania w konstruktorze w języku C#.

Ustawianie strategii buforowania w podklasowanej klasie ListView

Ustawienie atrybutu CachingStrategy z XAML w podklasie ListView nie spowoduje uzyskania żądanego zachowania, ponieważ nie CachingStrategy ma właściwości na .ListView Ponadto jeśli funkcja XAMLC jest włączona, zostanie wygenerowany następujący komunikat o błędzie: Nie znaleziono właściwości, właściwości możliwej do powiązania lub zdarzenia dla klasy "CachingStrategy"

Rozwiązaniem tego problemu jest określenie konstruktora w podklasie ListView ListViewCachingStrategy , który akceptuje parametr i przekazuje go do klasy bazowej:

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

ListViewCachingStrategy Następnie można określić wartość wyliczenia z języka XAML przy użyciu x:Arguments składni :

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

Sugestie dotyczące wydajności elementu ListView

Istnieje wiele technik poprawy wydajności elementu ListView. Poniższe sugestie mogą poprawić wydajność elementu ListView

  • Powiąż właściwość z ItemsSource kolekcją IList<T> zamiast IEnumerable<T> kolekcji, ponieważ IEnumerable<T> kolekcje nie obsługują dostępu losowego.
  • Użyj wbudowanych komórek (takich jak TextCell / SwitchCell ) zamiast ViewCell zawsze, gdy tylko możesz.
  • Użyj mniejszej liczby elementów. Rozważ na przykład użycie jednej FormattedString etykiety zamiast wielu etykiet.
  • Zastąp element wartością ListView TableView podczas wyświetlania danych innych niż homogeniczne — czyli danych różnych typów.
  • Ogranicz użycie Cell.ForceUpdateSize metody . W przypadku nadmiernej wydajności będzie ona obniżać wydajność.
  • W systemie Android należy unikać ustawiania ListViewwidoczności lub koloru separatora wierszy po utworzeniu wystąpienia, ponieważ powoduje to dużą karę za wydajność.
  • Unikaj zmieniania układu komórki na podstawie elementu BindingContext. Zmiana układu wiąże się z dużymi kosztami pomiaru i inicjowania.
  • Unikaj głęboko zagnieżdżonych hierarchii układu. Użyj polecenia AbsoluteLayout lub Grid , aby zmniejszyć zagnieżdżanie.
  • Unikaj określonych LayoutOptions elementów innych niż Fill (Fill jest najtańszy do obliczeń).
  • Unikaj umieszczania wewnątrz elementu ListView z ScrollView następujących powodów:
    • Element ListView implementuje własne przewijanie.
    • Obiekt ListView nie otrzyma żadnych gestów, ponieważ będą one obsługiwane przez obiekt nadrzędny ScrollView.
    • Element ListView może przedstawiać dostosowany nagłówek i stopkę, która przewija się z elementami listy, potencjalnie oferując funkcje, dla których ScrollView została użyta. Aby uzyskać więcej informacji, zobacz Nagłówki i stopki.
  • Rozważ niestandardowy moduł renderowania, jeśli potrzebujesz konkretnego, złożonego projektu przedstawionego w komórkach.

AbsoluteLayout ma potencjał do wykonywania układów bez pojedynczego wywołania miary, co czyni go bardzo wydajnym. Jeśli AbsoluteLayout nie można go użyć, rozważ użycie elementu RelativeLayout. Jeśli używasz RelativeLayoutmetody , przekazywanie ograniczeń bezpośrednio będzie znacznie szybsze niż użycie interfejsu API wyrażeń. Ta metoda jest szybsza, ponieważ interfejs API wyrażeń używa trybu JIT, a w systemie iOS drzewo musi być interpretowane, co jest wolniejsze. Interfejs API wyrażeń jest odpowiedni dla układów stron, w których jest wymagany tylko w początkowym układzie i rotacji, ale w ListViewsystemie , gdzie jest on uruchamiany stale podczas przewijania, boli wydajność.

Tworzenie niestandardowego modułu renderowania dla komórek ListView lub jest jednym z podejść do zmniejszenia wpływu obliczeń układu na wydajność przewijania. Aby uzyskać więcej informacji, zobacz Dostosowywanie elementu ListView i Dostosowywanie kontrolki ViewCell.