ListView 效能
撰寫行動應用程式時,效能很重要。 使用者期待順暢的捲動和快速載入時間。 無法符合使用者的期望,將花費您在應用程式市集中的評等,或在企業營運應用程式中花費您的組織時間和金錢。
Xamarin.FormsListView
是顯示數據的強大檢視,但它有一些限制。 使用自定義數據格時,捲動效能可能會受到影響,特別是當它們包含深層巢狀檢視階層,或使用需要複雜測量的特定版面配置時。 幸運的是,有一些技術可用來避免效能不佳。
快取策略
ListView 通常用來顯示比螢幕上適合的數據多得多。 例如,音樂應用程式可能有具有數千個專案的歌曲連結庫。 為每個專案建立專案會浪費寶貴的記憶體並執行不佳。 不斷建立和終結數據列會要求應用程式持續具現化和清除物件,這也會效能不佳。
為了節省記憶體,每個平臺的原生 ListView
對等專案都有重複使用數據列的內建功能。 只有畫面上可見的儲存格會載入記憶體中, 而內容 會載入現有的儲存格。 此模式可防止應用程式具現化數千個物件,以節省時間和記憶體。
Xamarin.FormsListView
允許透過 ListViewCachingStrategy
列舉重複使用儲存格,其具有下列值:
public enum ListViewCachingStrategy
{
RetainElement, // the default value
RecycleElement,
RecycleElementAndDataTemplate
}
注意
通用 Windows 平台 (UWP) 會RetainElement
忽略快取策略,因為它一律會使用快取來改善效能。 因此,根據預設,其行為就像 RecycleElement
套用快取策略一樣。
RetainElement
快 RetainElement
取策略會 ListView
指定 將針對清單中的每個項目產生單元格,而且是預設 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。
使用 DataTemplateSelector 的 RecycleElement
ListView
當 使用 DataTemplateSelector
來選取 DataTemplate
時,快RecycleElement
取策略不會快取 DataTemplate
s。 相反地, DataTemplate
會針對清單中的每個資料項目選取 。
注意
快RecycleElement
取策略有 2.4 中Xamarin.Forms引進的先決條件,當 被要求選取DataTemplate
必須傳DataTemplate
回相同ViewCell
類型的 時DataTemplateSelector
。 例如,假設 有 ListView
DataTemplateSelector
可以傳回 的 ,其中MyDataTemplateA
ViewCell
會傳回 MyDataTemplateA
型別的 MyViewCellA
,或 MyDataTemplateB
(其中 MyDataTemplateB
傳回 ViewCell
型別的 MyViewCellB
),則傳回時MyDataTemplateA
必須傳回 ,否則會擲回MyViewCellA
例外狀況。
RecycleElementAndDataTemplate
快 RecycleElementAndDataTemplate
取策略會 RecycleElement
以快取策略為基礎,藉由另外確保 ListView
使用 DataTemplateSelector
來選取 DataTemplate
時, DataTemplate
會由清單中的項目類型快取 。 因此, DataTemplate
會為每個項目類型選取一次 ,而不是每個項目實例一次。
注意
快RecycleElementAndDataTemplate
取策略具有 必要條件,DataTemplate
由傳DataTemplateSelector
回的 必須使用採用 Type
的DataTemplate
建構函式。
設定快取策略
列舉 ListViewCachingStrategy
值是以建 ListView
構函式多載來指定,如下列程式代碼範例所示:
var listView = new ListView(ListViewCachingStrategy.RecycleElement);
在 XAML 中 CachingStrategy
,設定 屬性,如下所示:
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
此方法的效果與在 C# 中的建構函式中設定快取策略自變數相同。
在子類別化 ListView 中設定快取策略
CachingStrategy
在子類別ListView
化上設定 XAML 的屬性不會產生所需的行為,因為上ListView
沒有 CachingStrategy
屬性。 此外,如果 已啟用 XAMLC ,會產生下列錯誤訊息: 找不到 『CachingStrategy』 的屬性、可系結屬性或事件
此問題的解決方案是在接受 ListViewCachingStrategy
參數並將它傳遞至基類的子類別ListView
上指定建構函式:
public class CustomListView : ListView
{
public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
{
}
...
}
ListViewCachingStrategy
然後,可以使用 語法從 XAML x:Arguments
指定列舉值:
<local:CustomListView>
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</local:CustomListView>
ListView 效能建議
有許多技術可改善的 ListView
效能。 下列建議可能會改善 ListView 的效能
- 將
ItemsSource
屬性系結至IList<T>
集合,IEnumerable<T>
而不是集合,因為IEnumerable<T>
集合不支持隨機存取。 - 使用內建單元格(例如
TextCell
/SwitchCell
),而不是ViewCell
隨時使用。 - 使用較少的元素。 例如,請考慮使用單
FormattedString
一標籤,而不是多個標籤。 ListView
TableView
在顯示非同質數據時,將 取代為 ,也就是不同類型的數據。- 限制方法的使用
Cell.ForceUpdateSize
。 如果過度使用,則會降低效能。 - 在Android上,避免在具現化數據列分隔符的可見性或色彩之後設定
ListView
,因為這會導致效能大幅降低。 - 請避免根據
BindingContext
變更儲存格配置。 變更版面配置會產生較大的測量和初始化成本。 - 避免深度巢狀配置階層。 使用
AbsoluteLayout
或Grid
來協助減少巢狀結構。 - 避免非特定
LayoutOptions
Fill
(Fill
是計算成本最低的)。 - 基於下列原因,請避免將 放在
ListView
內ScrollView
:- 會
ListView
實作自己的捲動。 ListView
不會收到任何手勢,因為它們將由父ScrollView
代 處理。ListView
可以呈現隨清單元素捲動的自定義頁首和頁尾,可能會提供 所使用的功能ScrollView
。 如需詳細資訊,請參閱 頁首和頁尾。
- 會
- 如果您需要儲存格中呈現的特定複雜設計,請考慮自定義轉譯器。
AbsoluteLayout
有可能在沒有單一量值呼叫的情況下執行版面配置,使其具有高效能。 如果 AbsoluteLayout
無法使用,請考慮 RelativeLayout
。 RelativeLayout
如果使用 ,直接傳遞條件約束的速度會比使用表達式 API 快得多。 這個方法較快,因為表達式 API 會使用 JIT,而且在 iOS 上必須解譯樹狀結構,這較慢。 表達式 API 適用於只有在初始版面配置和旋轉時才需要的頁面版面配置,但在 中 ListView
,其會在捲動期間持續執行,因而損害效能。
為 ListView
或其儲存格建置自定義轉譯器是減少版面配置計算對卷動效能的影響的一種方法。 如需詳細資訊,請參閱 自定義 ListView 和 自定義 ViewCell。