ListView性能
使用基于ListView的应用程序确保卓越的性能。
-
PDF用于离线使用
- 相关样品:
- 相关API:
让我们知道你对此的感受
最后更新:2016年12月
在编写移动应用时,性能很重要。用户预期平滑滚动和快速加载时间。如果不能满足用户的期望,您的应用商店中的评分将会降低,或者在业务线应用程序的情况下,会使您的组织花费时间和金钱。
虽然ListView
是显示数据的强大视图,但它有其局限性。使用自定义单元格时,滚动性能会受到影响,特别是当它们包含深层次的视图层次结构或使用需要大量测量的某些布局时。幸运的是,有一些技术可以用来避免性能不佳。
本文将讨论以下主题:
缓存策略
ListViews通常用于显示比可以适合屏幕的数据更多的数据。考虑一个音乐应用程序,例如。歌曲库可能有成千上万的条目。一个简单的方法,即为每一首歌曲创建一行,将表现不佳。这种方法浪费宝贵的内存,并可以缓慢滚动到爬网。另一种方法是在数据滚动到视图中时创建和销毁行。这需要对视图对象进行不间断的实例化和清理,这可能非常慢。
为了节省内存,ListView
每个平台的本机等价物具有内置的重新使用行的功能。只有屏幕上可见的单元格才能加载到内存中,并将内容加载到现有的单元格中。这样可以防止应用程序实例化数千个对象,节省时间和内存。
Xamarin.Forms 2 ListView
通过ListViewCachingStrategy
枚举引入单元格重用,它具有以下值:
public enum ListViewCachingStrategy
{
RetainElement // the default value
RecycleElement
}
RetainElement
该RetainElement
值指定ListView
将为列表中的每个项生成一个单元格。这是ListView
Xamarin.Forms 2之前的行为,是默认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。
设置缓存策略
该ListViewCachingStrategy
值由ListView
构造函数重载指定,如以下代码示例所示:
var listView = new ListView(ListViewCachingStrategy.RecycleElement);
在XAML中,CachingStrategy
按如下代码设置属性:
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
这与在C#中的构造函数中设置缓存策略参数具有相同的效果。注意没有CachingStrategy
财产ListView
。
在Subclassed ListView中设置缓存策略
在CachingStrategy
XAML子类上设置属性ListView
不会产生所需的行为,因为没有CachingStrategy
属性ListView
。另外,如果启用了XAMLC,将会产生以下错误消息:没有为'CachingStrategy'找到属性,可绑定属性或事件
解决这个问题的方法是在子类上指定一个ListView
接受ListViewCachingStrategy
参数并将其传递给基类的构造函数:
public class CustomListView : ListView
{
public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
{
}
...
}
然后ListViewCachingStrategy
可以使用以下x:Arguments
语法从XAML指定该值:
<local:CustomListView>
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</local:CustomListView>
提高ListView性能
有很多技术来提高性能ListView
:
- 将
ItemsSource
属性绑定到IList<T>
集合而不是IEnumerable<T>
集合,因为IEnumerable<T>
集合不支持随机访问。 - 使用内置单元格(如
TextCell
/SwitchCell
),而不是ViewCell
随时可用。 - 使用较少的元素。例如考虑使用单个
FormattedString
标签而不是多个标签。 - 当显示非均匀数据时,替换为
ListView
aTableView
,即不同类型的数据。 - 限制使用该
Cell.ForceUpdateSize
方法。如果过度使用,会降低性能。 - 在Android上,避免
ListView
在实例化之后设置行分隔符的可见性或颜色,因为会导致性能下降。 - 避免改变单元布局
BindingContext
。这导致了很大的布局和初始化成本。 - 避免深层嵌套的布局层次结构。使用
AbsoluteLayout
或Grid
帮助减少嵌套。 - 避免特定
LayoutOptions
比其他Fill
(填充是最便宜的计算)。 - 避免放置
ListView
内部ScrollView
,原因如下:- 在
ListView
实现了自己的滚动。 - 该
ListView
不会收到任何手势,因为他们会被家长进行处理ScrollView
。 - 所述
ListView
可呈现定制的页眉和页脚,与列表的元素滚动,潜在地提供该功能ScrollView
用于。有关更多信息,请参阅页眉和页脚。
- 在
- 如果您需要在单元格中提供的非常具体,复杂的设计,请考虑自定义渲染器。
AbsoluteLayout
有可能执行布局,而无需单次测量。这使它非常强大的性能。如果AbsoluteLayout
不能使用,请考虑RelativeLayout
。如果使用RelativeLayout
,直接传递约束将比使用表达式API快得多。这是因为表达式API使用JIT,而在iOS上,必须解释树,这是较慢的。表达式API适用于仅在初始布局和旋转时需要的页面布局,但是在ListView
滚动时不断运行的页面布局会降低性能。
ListView
为其单元格构建自定义渲染器是减少布局计算对滚动性能的影响的一种方法。有关更多信息,请参阅自定义ListView并自定义ViewCell。