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 DataTemplate
elementu , RecycleElement
strategia buforowania nie buforuje DataTemplate
wartoś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
MyViewCellB
typ ), 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 DataTemplate
elementu , DataTemplate
s są buforowane przez typ elementu na liście. DataTemplate
W 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 DataTemplate
zwracany przez DataTemplateSelector
element musi używać konstruktora DataTemplate
, który przyjmuje Type
element .
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>
zamiastIEnumerable<T>
kolekcji, ponieważIEnumerable<T>
kolekcje nie obsługują dostępu losowego. - Użyj wbudowanych komórek (takich jak
TextCell
/SwitchCell
) zamiastViewCell
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
ListView
widocznoś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
lubGrid
, 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
zScrollView
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ędnyScrollView
. - Element
ListView
może przedstawiać dostosowany nagłówek i stopkę, która przewija się z elementami listy, potencjalnie oferując funkcje, dla którychScrollView
została użyta. Aby uzyskać więcej informacji, zobacz Nagłówki i stopki.
- Element
- 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 RelativeLayout
metody , 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 ListView
systemie , 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.