Desempenho do ListView
Ao escrever aplicativos móveis, o desempenho é importante. Os usuários passaram a esperar rolagem suave e tempos de carregamento rápidos. Não atender às expectativas dos usuários custará classificações no repositório de aplicativos ou, no caso de um aplicativo de linha de negócios, custará tempo e dinheiro à sua organização.
O Xamarin.FormsListView
é uma exibição poderosa para exibir dados, mas tem algumas limitações. O desempenho da rolagem pode ser prejudicado ao usar células personalizadas, especialmente quando elas contêm hierarquias de exibição profundamente aninhadas ou usam determinados layouts que exigem medições complexas. Felizmente, existem técnicas que você pode usar para evitar o mau desempenho.
Estratégia de cache
ListViews são frequentemente usados para exibir muito mais dados do que cabe na tela. Por exemplo, um aplicativo de música pode ter uma biblioteca de músicas com milhares de entradas. Criar um item para cada entrada desperdiçaria memória valiosa e teria um desempenho ruim. Criar e destruir linhas constantemente exigiria que o aplicativo instanciasse e limpasse objetos constantemente, o que também teria um desempenho ruim.
Para conservar memória, os equivalentes nativos ListView
de cada plataforma têm recursos internos para reutilizar linhas. Somente as células visíveis na tela são carregadas na memória e o conteúdo é carregado nas células existentes. Esse padrão impede que o aplicativo instancie milhares de objetos, economizando tempo e memória.
Xamarin.Forms Permite a reutilização de ListView
células por meio da ListViewCachingStrategy
enumeração, que tem os seguintes valores:
public enum ListViewCachingStrategy
{
RetainElement, // the default value
RecycleElement,
RecycleElementAndDataTemplate
}
Observação
A Plataforma Universal do Windows (UWP) ignora a estratégia de cache, porque sempre usa o cache para melhorar o RetainElement
desempenho. Portanto, por padrão, ele se comporta como se a RecycleElement
estratégia de cache fosse aplicada.
RetainElement
A RetainElement
estratégia de cache especifica que o ListView
gerará uma célula para cada item na lista e é o comportamento padrão ListView
. Deve ser utilizado nas seguintes circunstâncias:
- Cada célula tem um grande número de ligações (20-30+).
- O modelo de célula é alterado com frequência.
- O teste revela que a
RecycleElement
estratégia de cache resulta em uma velocidade de execução reduzida.
É importante reconhecer as consequências da estratégia de RetainElement
cache ao trabalhar com células personalizadas. Qualquer código de inicialização de célula precisará ser executado para cada criação de célula, que pode ser várias vezes por segundo. Nessa circunstância, as técnicas de layout que eram boas em uma página, como o uso de várias instâncias aninhadas StackLayout
, tornam-se gargalos de desempenho quando são configuradas e destruídas em tempo real à medida que o usuário rola.
RecycleElement
A RecycleElement
estratégia de cache especifica que o tentará minimizar o volume de memória e a velocidade de execução reciclando as células da ListView
lista. Esse modo nem sempre oferece uma melhoria de desempenho, e os testes devem ser realizados para determinar quaisquer melhorias. No entanto, é a escolha preferida e deve ser usada nas seguintes circunstâncias:
- Cada célula tem um número pequeno a moderado de ligações.
- Cada célula define
BindingContext
todos os dados da célula. - Cada célula é em grande parte semelhante, com o modelo de célula imutável.
Durante a virtualização, a célula terá seu contexto de vinculação atualizado e, portanto, se um aplicativo usar esse modo, ele deverá garantir que as atualizações de contexto de vinculação sejam tratadas adequadamente. Todos os dados sobre a célula devem vir do contexto de vinculação ou erros de consistência podem ocorrer. Esse problema pode ser evitado usando a vinculação de dados para exibir dados de célula. Como alternativa, os OnBindingContextChanged
dados da célula devem ser definidos na substituição, em vez de no construtor da célula personalizada, conforme demonstrado no exemplo de código a seguir:
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;
}
}
}
Para obter mais informações, consulte Alterações de contexto de vinculação.
No iOS e no Android, se as células usarem renderizadores personalizados, elas deverão garantir que a notificação de alteração de propriedade seja implementada corretamente. Quando as células são reutilizadas, seus valores de propriedade serão alterados quando o contexto de vinculação for atualizado para o de uma célula disponível, com PropertyChanged
eventos sendo gerados. Para obter mais informações, consulte Personalizando uma ViewCell.
RecycleElement com um DataTemplateSelector
Quando um ListView
usa um DataTemplateSelector
para selecionar um DataTemplate
, a estratégia de cache não armazena RecycleElement
em cache DataTemplate
s. Em vez disso, um DataTemplate
é selecionado para cada item de dados na lista.
Observação
A RecycleElement
estratégia de cache tem um pré-requisito, introduzido na Xamarin.Forms versão 2.4, que quando um DataTemplateSelector
é solicitado a selecionar um DataTemplate
que cada DataTemplate
um deve retornar o mesmo ViewCell
tipo. Por exemplo, dado um ListView
com um DataTemplateSelector
que pode retornar ( MyDataTemplateA
onde MyDataTemplateA
retorna um ViewCell
do tipo MyViewCellA
), ou MyDataTemplateB
(onde MyDataTemplateB
retorna um ViewCell
do tipo MyViewCellB
), quando MyDataTemplateA
é retornado ele deve retornar MyViewCellA
ou uma exceção será lançada.
RecycleElementAndDataTemplate
A RecycleElementAndDataTemplate
estratégia de cache se baseia na estratégia de cache, garantindo adicionalmente que, RecycleElement
quando um ListView
usa a DataTemplateSelector
para selecionar um DataTemplate
, DataTemplate
s são armazenados em cache pelo tipo de item na lista. Portanto, DataTemplate
s são selecionados uma vez por tipo de item, em vez de uma vez por instância de item.
Observação
A RecycleElementAndDataTemplate
estratégia de cache tem um pré-requisito que o DataTemplate
DataTemplateSelector
s retornado pelo deve usar o DataTemplate
construtor que leva um Type
arquivo .
Definir a estratégia de cache
O ListViewCachingStrategy
valor de enumeração é especificado com uma sobrecarga de ListView
construtor, conforme mostrado no exemplo de código a seguir:
var listView = new ListView(ListViewCachingStrategy.RecycleElement);
Em XAML, defina o CachingStrategy
atributo conforme mostrado no XAML abaixo:
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Esse método tem o mesmo efeito que definir o argumento de estratégia de cache no construtor em C#.
Definir a estratégia de cache em um ListView subclassificado
Definir o CachingStrategy
atributo de XAML em uma subclasse ListView
não produzirá o comportamento desejado, porque não há nenhuma CachingStrategy
propriedade em ListView
. Além disso, se o XAMLC estiver habilitado, a seguinte mensagem de erro será produzida: Nenhuma propriedade, propriedade vinculável ou evento encontrado para 'CachingStrategy'
A solução para esse problema é especificar um construtor no subclassed ListView
que aceita um ListViewCachingStrategy
parâmetro e o passa para a classe base:
public class CustomListView : ListView
{
public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
{
}
...
}
Em seguida, o ListViewCachingStrategy
valor de enumeração pode ser especificado a partir de XAML usando a x:Arguments
sintaxe:
<local:CustomListView>
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</local:CustomListView>
ListView sugestões de desempenho
Existem muitas técnicas para melhorar o desempenho de um ListView
arquivo . As sugestões a seguir podem melhorar o desempenho do ListView
- Vincule a
ItemsSource
propriedade a umaIList<T>
coleção em vez de umaIEnumerable<T>
coleção, porqueIEnumerable<T>
as coleções não oferecem suporte a acesso aleatório. - Use as células internas (como
TextCell
/SwitchCell
) em vez deViewCell
sempre que puder. - Use menos elementos. Por exemplo, considere usar um único
FormattedString
rótulo em vez de vários rótulos. - Substitua o por um
TableView
ao exibir dados não homogêneosListView
– ou seja, dados de diferentes tipos. - Limite o
Cell.ForceUpdateSize
uso do método. Se usado em excesso, ele degradará o desempenho. - No Android, evite definir a visibilidade ou a cor de um
ListView
separador de linha do depois que ele tiver sido instanciado, pois isso resulta em uma grande penalidade de desempenho. - Evite alterar o layout da célula com base no
BindingContext
. A alteração do layout incorre em grandes custos de medição e inicialização. - Evite hierarquias de layout profundamente aninhadas. Use
AbsoluteLayout
ouGrid
para ajudar a reduzir o aninhamento. - Evite outros específicos
LayoutOptions
que nãoFill
(Fill
é o mais barato para computar). - Evite colocar um
ListView
dentro de umScrollView
pelos seguintes motivos:- O
ListView
implementa sua própria rolagem. - O
ListView
não receberá nenhum gesto, pois eles serão manuseados pelo paiScrollView
. - O
ListView
pode apresentar um cabeçalho e rodapé personalizados que rola com os elementos da lista, potencialmente oferecendo a funcionalidade para a qual oScrollView
foi usado. Para obter mais informações, consulte Cabeçalhos e rodapés.
- O
- Considere um renderizador personalizado se precisar de um design específico e complexo apresentado em suas células.
AbsoluteLayout
tem o potencial de executar layouts sem uma única chamada de medida, tornando-o de alto desempenho. Se AbsoluteLayout
não puder ser usado, considere RelativeLayout
. Se usar RelativeLayout
o , passar Restrições diretamente será consideravelmente mais rápido do que usar a expressão API. Esse método é mais rápido porque a API de expressão usa JIT e, no iOS, a árvore precisa ser interpretada, o que é mais lento. A API de expressão é adequada para layouts de página em que ela só é necessária no layout inicial e na rotação, mas no ListView
, onde é executada constantemente durante a rolagem, prejudica o desempenho.
Criar um renderizador personalizado para uma ListView
ou suas células é uma abordagem para reduzir o efeito dos cálculos de layout no desempenho de rolagem. Para obter mais informações, consulte Personalizando um ListView e Personalizando um ViewCell.