Windows phone UI虛擬化和數據虛擬化(二)


書接上回的Windows phone UI虛擬化和數據虛擬化(一)我們學習了wp的ui虛擬化。
今天來和大家分享一下wp的數據虛擬化。

並同時感謝我的同事dgwutao在編寫此文時給我的巨大幫助,3ks!

1.什么是數據虛擬化及其優點。

--弱水三千,只取一瓢飲。百萬記錄,只載十幾條。

和ui虛擬化一樣,盡管我們要顯示的數據有成百上千條,但我們只在內存中,加載我們需要展示在屏幕上的

若干條。隨着列表的滑動,我們添加將要顯示在屏幕上的數據條目,刪除已經不再展示區的條目。保證內存中

加載的數據條目只有很少的一部分(具體數目根據情況定制)。

虛擬化的優點也顯而易見了,有效地節省內存。

2.怎么實現數據虛擬化

   

不知道各位看到這里,心里會不會說,通過上面的描述,這個,這個,不就是【點擊】/【滑動】 加載更多嗎?

話說滑動加載真心弱爆了。。。也不要糾結怎么判斷觸底沒觸底了。

來!開始我們的數據虛擬化!

接着用上一篇的Demo,我們來看一個基本的列表數據綁定。

【.xaml】
<phone:LongListSelector ItemsSource="{Binding ListProduct}"

【.cs】

這個是,我們通常的用法。沒有任何問題。但是,我們要實現我們想要的只加載屏幕上顯示的一頁(9條)數據。

該如何着手呢?保持List只有9條很容易,但是當list只有9條恰好一頁的時候,我們知道ListBox,LLS的滾動條。
根本就不會滾動。因為列表控件認為你的數據源不是1000條,而是9條。

那么列表控件是根據什么來判斷數據源的總數呢?
開始思考。。。3 。。。    2。。。。。1。。。對,答案就是集合的【Count】屬性。

所以我們第一步要做的就是,騙過這些列表控件。給他們一個"假的" 【Count】屬性

下面我們定義一個泛型類,繼承Ilist<T>等幾個接口。

public class VirtualizingCollection<T>:IList<T>,IList, INotifyPropertyChanged, INotifyCollectionChanged

{

public VirtualizingCollection(int count)

{

this.count = count;

}

public int Count

{

get { return count; }

set

{

count = value;

RaisePropertyChanged("Count");

}

}

public T this[int index]

{

get

{

Return null;

}

set

{

throw new NotImplementedException();

}

}

//----省略-----

}

So Easy! 我們只要在VirtualizingCollection初始化的時候給Count付個1000,就輕松的騙過那些傻傻的列表控件,可以盡情的滾動了。

盡管能刷刷的滾動了,但是我們發現,界面上一片空白,沒有任何數據。為啥呢?

控件在綁定后,遍歷數據源時。我們的索引 public T this[int index]還沒有實現!肯定是不會返回任何數據的。

這塊索引的實現並不是固定的實現方式,要根據具體的場景來看。

好,現在我們來假設這樣一個qq聊天的場景。

在進入qq會話頁面的時候會從本地數據庫里讀取聊天的歷史記錄。共有1000條記錄。每頁顯示9條。

這里我們可以按照傳統的分頁方式來加載。

在上面的類里,我們新建一個字典字段。

private readonly Dictionary<int, IList<T>> _pages = new Dictionary<int, IList<T>>();

這個字典就是我們所說的,對應屏幕上顯示的若干條數據(此例中我們保持每個時刻,內存中只有3-5屏數據)

Dictionary<int, IList<T>> int就是pageindex 頁碼, IList<T> 就是這一頁里的pagesize條數據(本例是9條)

下面是具體的代碼

private readonly int _pageSize = 9;

public T this[int index]

{

get

{

int pageIndex = index / PageSize;//請求的item的索引(index)所在的頁碼

int pageOffset = index % PageSize;//滑動列表當前頁產生的偏移

RequestPage(pageIndex);

//大於一屏的一半加載下一頁

if (pageOffset > PageSize / 2 && pageIndex < Count / PageSize)

{

RequestPage(pageIndex + 1);

}

// 小於一屏的一半加載上一頁

if (pageOffset < PageSize / 2 && pageIndex > 0)

{

RequestPage(pageIndex - 1);

   

}

//只保留,當前頁和上下兩頁,刪除多余的

if (_pages.Count >= 3 && _pages.ContainsKey(pageIndex - 2))

{

_pages.Remove(pageIndex - 2);

}

if (_pages.Count > 3 && _pages.ContainsKey(pageIndex + 2))

{

_pages.Remove(pageIndex + 2);

}

return _pages[pageIndex][pageOffset];

}

}

protected virtual void RequestPage(int pageIndex)

{

if (!_pages.ContainsKey(pageIndex))

{

_pages.Add(pageIndex, null);

LoadPage(pageIndex);

}

   

}

protected virtual void LoadPage(int pageIndex)

{

PopulatePage(pageIndex, FetchPage(pageIndex));

}

   

protected virtual void PopulatePage(int pageIndex, IList<T> page)

{

if (_pages.ContainsKey(pageIndex))

_pages[pageIndex] = page;

}

protected IList<T> FetchPage(int pageIndex)

{ //這里是從數據庫里取,列表請求的頁碼對應的數據

return (IList<T>)LocalData.FetchRange(pageIndex * PageSize, PageSize);

}

LocalDatd.cs

public static class LocalData

{

private static Random rnd = new Random();

private static List<Product> listProduct;

static LocalData()

{ //假設這里是存在本地數據庫里的聊天記錄的信息有1000條

listProduct = new List<Product>(1000);

for (int i = 0; i < 1000; i++)

{

listProduct.Add(new Product { Id = i, Name = "聊天記錄-" + rnd.Next(1000, 10000).ToString(), Category = "" });

}

}

//分頁取庫里的聊天記錄(模擬)

public static IList<Product> FetchRange(int skip, int take)

{

return listProduct.Skip(skip).Take(take).ToList();

}

}

在viewmodel中初始化

private VirtualizingCollection<Product> vList;

public VirtualizingCollection<Product> VList

{

get { return vList; }

set { vList = value; }

}

private static Random rnd=new Random ();

public MainPageViewModel()

{

vList = new VirtualizingCollection<Product>(1000);

}

我們運行,上下滑動,看一下效果s

   

我們看到內存中加載的頁數始終是3-5頁。而且滾動條已經顯示的是總數為1000時的高度。

這就是所謂的數據虛擬化了。

比起點擊加載,滑動加載,以數據虛擬化的方式,內存占用更少。(滑動加載數據源是不斷被add的並沒有delete)

另一點在用戶體驗上,滾動條的變化也給了用戶預期,列表到底有多少數據。

當然我這里只是演示性的demo。具體應用還有很多需要注意的地方。比如說如果數據來源於網絡,從磁盤讀取,較慢的時候。

就需要一些異步的的讀取處理。在實現索引器的時候我們這里是根據固定的頁數移除,還可以根據最近的加載時間判斷哪些頁面要移除等等。

   

最后我還要留個小小的問題。public class VirtualizingCollection<T>:IList<T>,IList在這里我實現了兩個很相近的接口。

誰能告訴我這么做是為啥?請留言回答:) 回答正確的,月薪15k以下的,你們老板對不起你!

最后的吶喊!所有關注/從事Windows Phone平台的道友,能不能在android,ios大行其道,wp淪為小平台的無奈下,

別再敝帚自珍!一起為wp平台多奉獻一些自己的經驗,多分享一些優質的資源。

能不能在您看完文章的此刻,順便給這些同在wp平台掙扎的道友,輕輕的,點一下【推薦】!!!

demo下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM