Windows phone UI虚拟化和数据虚拟化

转自:Windows phone UI虚拟化和数据虚拟化(一)

今天和大家分享一些关于windows phone ui虚拟化和数据虚拟化的一些知识。

也顺便回答我上一篇【LongListSelector 控件 在 wp7 和wp8中的不同之处】里,留下的那个问题,微软为什么推荐使用longlistselector.

如果不是新人,那么对于"虚拟化"这个词应该不陌生。

"虚拟化"简单来说,就是在数据量很大的时候,我们只加载与可视区域(如手机屏幕)相应的少量数据。

我们先来看一下UI虚拟化。

一:Windows phone UI虚拟化

对于wp的ui虚拟化,其实我们并不需要花太多心思,因为系统自带且用的最多的listbox 和longlistselector。

已经是帮我们实现了虚拟化。我们只需要注意别把美好的虚拟化破坏掉就好了。

1.首先,演示一下虚拟化。

MainPageViewMode.cs

public class MainPageViewModel

{

//省略

public MainPageViewModel()

{ //我们模拟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 = "" });

}

Mainpage.xaml

<DataTemplate x:Key="LBDataTemplate">

<Grid Loaded="Grid_Loaded" d:DesignWidth="411.045" d:DesignHeight="78.209">

<TextBlock HorizontalAlignment="Left" Margin="10,17,0,25" TextWrapping="Wrap" Foreground="Black"

Text="{Binding Name}" Height="36" Width="197" FontSize="30"/>

</Grid>

</DataTemplate>

}" />

Loaded="Grid_Loaded" 此事件触发说明模板被加载。这里我们用的是ListBox控件

 

MainPage.cs

MainPageViewModel vm;

public MainPage()

{

InitializeComponent();

vm= new MainPageViewModel();

LayoutRoot.DataContext = vm;

}

private void Grid_Loaded(object sender, RoutedEventArgs e)

{

vm.LoadItemCounter++;//界面上item被渲染的数目

}

Windows phone UI虚拟化和数据虚拟化_第1张图片

 

我们看到我们绑定的数据源有1000条数据,页面加载后item只渲染了21个。看似是有这样一个规律的 可视区域数目*2+3

随着我们滑动列表,Item也在不断被渲染。

Windows phone UI虚拟化和数据虚拟化_第2张图片

嗯哪,这就是所谓的UI虚拟化了。

 

2.常见的破坏虚拟化的场景

很多时候,我们在编辑模板的时候,会破坏掉虚拟化。看下面

 

Windows phone UI虚拟化和数据虚拟化_第3张图片

当我们编辑ListBox的ItemsPanel的时候,工具帮我们自动生成了下面的代码。

<ItemsPanelTemplate x:Key="ItemsPanelTemplate">

<StackPanel/>

</ItemsPanelTemplate>

这时候我们再次运行

WoW ! 这次页面一加载,ListBox就在不停地渲染Item.直至全部!

为什么会产生这种情况?

因为实现listbox的关键是默认的容器 VirtualizingStackPanel 通过工具编辑变成了StackPanel

改回VirtualizingStackPanel

<ItemsPanelTemplate x:Key="ItemsPanelTemplate">

<VirtualizingStackPanel/>

>

Windows phone UI虚拟化和数据虚拟化_第4张图片

呼! 星星又亮了!

好,到这里们我己经掌握了,什么是UI虚拟化,如何避免常见的破坏UI虚拟化的场景。

以及如何判断我们对控件更改后,节操和虚拟化还在不在。

妈妈再也不用担心我们不小心破坏虚拟化了!

下面我们来看一下LonglistSelector

还是上面的例子。我们把<ListBox 换成 <phone:LongListSelector 去掉ItemsPanel="{StaticResource ItemsPanelTemplate}"

 

Windows phone UI虚拟化和数据虚拟化_第5张图片

还是虚拟化,只不过默认生成的item数不一样了。

那么为什么微软官方推荐我们用longlistselector代替ListBox呢?

我们继续滑动。

看输出!

什么!!!还是31,没反应?!!!

是的,Longlistselector的 DataTemplate得到了复用。我们的item的datatemplate不会再渲染新的了。

打完,收功!

东西有点多,分两篇吧,下一篇是对性能提升更有用处的数据虚拟化。

附件Demo

蝗虫的大腿--windows phone 平台的坚定支持者!


转自: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>,IListINotifyPropertyChangedINotifyCollectionChanged

{

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<intIList<T>> _pages = new Dictionary<intIList<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

Windows phone UI虚拟化和数据虚拟化_第6张图片Windows phone UI虚拟化和数据虚拟化_第7张图片

   

我们看到内存中加载的页数始终是3-5页。而且滚动条已经显示的是总数为1000时的高度。

这就是所谓的数据虚拟化了。

比起点击加载,滑动加载,以数据虚拟化的方式,内存占用更少。(滑动加载数据源是不断被add的并没有delete)

另一点在用户体验上,滚动条的变化也给了用户预期,列表到底有多少数据。

当然我这里只是演示性的demo。具体应用还有很多需要注意的地方。比如说如果数据来源于网络,从磁盘读取,较慢的时候。

就需要一些异步的的读取处理。在实现索引器的时候我们这里是根据固定的页数移除,还可以根据最近的加载时间判断哪些页面要移除等等。

   

最后我还要留个小小的问题。public class VirtualizingCollection<T>:IList<T>,IList在这里我实现了两个很相近的接口。

谁能告诉我这么做是为啥?请留言回答:) 回答正确的,月薪15k以下的,你们老板对不起你!

最后的呐喊!所有关注/从事Windows Phone平台的道友,能不能在android,ios大行其道,wp沦为小平台的无奈下,

别再敝帚自珍!一起为wp平台多奉献一些自己的经验,多分享一些优质的资源。

能不能在您看完文章的此刻,顺便给这些同在wp平台挣扎的道友,轻轻的,点一下【推荐】!!!

demo下载

蝗虫的大腿--windows phone 平台的坚定支持者!


你可能感兴趣的:(windows,虚拟化,phone)