这篇文章的 demo 是在 (一)的基础上进行的调整,逻辑基本相似。本文只列和 上一篇出不同的代码。

为了实现自定义的虚拟化,把上一篇文章的 ListBox 换成 ScrollViewer + ItemsControl,这样组合在实际的项目

中又是还是会用到的,比如,如果我们需要对 ScrollViewer 进行很多的控制,比如获取它的“滑动”事件,ScrollViewer

中在放置其它控件,或者直接定制它的样式等等(当然可以通过 VisualTreeHelper 也可以获取 ListBox 中的 ScrollViewer)。

ListBox (继承自 ItemsControl)内部的实现就是封装了 ScrollViewer + ItemsControl 控件,在本 demo 中,使用的组合为:

            <ScrollViewer x:Name="scrollViewer" Loaded="ScrollViewer_Loaded">
<ItemsControl x:Name="listbox" ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!---虽然设置为“虚拟面板”,但是它是不起虚拟作用的-->
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="10,30,0,0">
<Image VerticalAlignment="Top" Source="{Binding Photo}" Width="150"/>
<TextBlock Text="{Binding Title}" Width="250" Foreground="Wheat"
FontSize="25" Margin="10,0,0,0" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>

在上一篇 demo 的基础上,当加载 200条数据时,在 1G 的模拟器上运行时,内存占用竟达到 200+MB

如果在 512MB 的模拟器上,还没加载数据完成,应用就崩溃了:

优化算法

下面 demo 的原理很简单,就是当列表中的项,在屏幕内的时候,把它的 Visibility 设置为 Visibility.Visible,

当在屏幕外面的时候,设置为 Visibility.Collapsed; 逻辑很简单,但是对内存的占用明显下降。但是,为了用户

体验,也就是如果当用户滑动列表到屏幕的地方,它的项目没有及时的显示,在用户的角度看,是会非常沮丧的,所以

需要一个算法检查当前列表中的项是否在屏幕内。

思路:

关于这个 demo 其它部分的代码请参考上一篇文章。

1)首先在 xaml 页面放一个按钮,如上图所示,当应用加载完成时,默认不错任何处理,当点击 “虚拟化” 按钮时,

触发自定义虚拟化方法,页面中的 xaml:

<Button Content="虚拟化" HorizontalAlignment="Left" Margin="335,0,0,0"
VerticalAlignment="Top" Width="133" Height="72" Tap="Button_Tap"/>

相应的 C#:

        //当用户单击 按钮时,开启模拟虚拟化
private void Button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
e.Handled = true; Visualizition();
}

2)当点就按钮后,首先获取列表中,所有由 DataTemplate 中的 StackPanel 复制的每一项。因为 ListBox 继承自 ItemsControl,

并且它们 ItemContainerGenerator 属性的 ContainerFromIndex(int index) 方法可以获取列表中的指定的 Item,然后在通过

VisualTreeHelper 的静态方法,获取模版产生的 StackPanel。全部的代码:

        void Visualizition()
{
// 自定义一个“字典”,用来保存每项的 Y坐标 和它“本身”的引用
Dictionary<double, StackPanel> dic = new Dictionary<double, StackPanel>(); double height_sum = ;
for (int i = ; i < listbox.Items.Count; i++)
{
// 因为 ListBox 是通过数据绑定生成的列表,所以需要通过 ItemContainerGenerator 获得
// 每一项的 StackPanel 这个父控件
FrameworkElement fe = listbox.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
StackPanel sp = FindFirstElementInVisualTree<StackPanel>(fe); dic.Add(height_sum, sp); // 累加 Y高度
// 30 为模版中父容器的 margin-top
height_sum = height_sum + sp.ActualHeight + ; // 设置它的高度为自己的实际高度
// 很重要,如果不为父容器指定固定高度,当子元素隐藏后,父容器高度变为0px
sp.Height = sp.ActualHeight;
} // 每0.5秒钟,循环检查一次列表中,哪些项在屏幕内,如果在屏幕内,则显示,如果
// 在屏幕外,则隐藏
Observable.Interval(TimeSpan.FromSeconds(.)).ObserveOnDispatcher().Subscribe((_) =>
{
foreach (var keyValue in dic)
{
if (((scrollViewer.VerticalOffset - ) > keyValue.Key || keyValue.Key > (scrollViewer.VerticalOffset + )))
{
keyValue.Value.Children[].Visibility = System.Windows.Visibility.Collapsed;
keyValue.Value.Children[].Visibility = System.Windows.Visibility.Collapsed;
}
else
{ keyValue.Value.Children[].Visibility = System.Windows.Visibility.Visible;
keyValue.Value.Children[].Visibility = System.Windows.Visibility.Visible;
}
}
});
} // 查找“视图树”中的控件
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == ) return null; for (int i = ; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i); if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
} return null;
}

当加载 200 条新闻的时候,运行工程效果:

上面算法是每 0.5秒 遍历一下 Dictionary 的 keys,为了直观就没有再优化。比如每次遍历的时间,

屏幕的可视区域等。

默认运行时,内存占用 208MB 效果:

单击按钮后,当上下滑动的时候,可以看到延迟显示的 item,内存占用减少了不少:

另外,我想到可以使用 快速排序 的算法方法,可以更快找到新滑动到屏幕里的 Item,之前在屏幕外的 Item

如果还在屏幕外,则跳过,等等。关于如何优化上面算法这里就不在多讲了。因为项目只是在介绍减少内存的

思路,所以没有考虑在应用中如何在“加载更多..”时,如何再次添加新 item 等等实际交互。

还有就是关于 Reactive Extension 相关类库(已经集成在了 WP8 的sdk 中)的使用,这里也不过多介绍,它

确实是一个神奇的东西,园子里有朋友写过相关的文章,我前段时间也翻译了一下(译文链接),稍后会整理

更多关于 Rx 的文章。这里使用 Observable  作为计时器,当然也可以自定义 Timer ,不过感觉 Observable 用起来

更加方便。

上面代码中:Observable.Interval(TimeSpan.FromSeconds(.)).ObserveOnDispatcher().Subscribe((_) => { //省略  });

的含义是,每隔 0.5秒钟,在 UI 线程中 调用一次 Subscribe 注册的方法。

引申

通过这个 demo,开发者应该知道了,在页面中,尽量少的绘制元素,对于 Windows Phone 应用程序性能的提升,对于内存占用

的优化,有多么的明显。例如,尽量减少 UI 控件的嵌套;在 Pivot (或者 Panorama )页面控件中的项,如果 PivotItem 不在

当前屏幕中,则把它的 Child 设为隐藏,当用户切换到该 PivotItem 页面时,在给它显示出来。等等。

本文工程 demo 下载

Windows Phone 性能优化(二)的更多相关文章

  1. MySQL性能优化(二):优化数据库的设计

    原文:MySQL性能优化(二):优化数据库的设计 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.n ...

  2. EMW 性能优化二之---并发配置

    EMW 性能优化二之---并发配置 在前一个日志中写到交货的异步更新,对于RFUI RF的前台操作会提升效率,异步更新不用等待更新状态的返回,启用更新队列的方式执行(SM13). 下面再补全性能相关的 ...

  3. Windows Phone 性能优化(一)

    在实际的项目开发过程中,应用的性能优化是一个永恒的话题,也是开发者群里最常讨论的话题之一,我在之 前的公司做 wp项目时,也遇到过性能的瓶颈.当页面中加载的内容越来越多时,内存涨幅非常明显(特别是 一 ...

  4. Spark性能优化(二)

    资源调优 调优概述 在开发完Spark作业之后,就该为作业配置合适的资源了.Spark的资源参数,基本都可以在spark-submit命令中作为参数设置.很多Spark初学者,通常不知道该设置哪些必要 ...

  5. Windows 7 性能优化

    1."计算机" 2.右键>"属性" 3."高级系统设置">"高级" 4."性能"> ...

  6. Windows Phone性能优化建议

    使用background thread解码图片 在Windows Phone中支持的图片格式有jpg和png,微软建议使用jpg格式的图片,因为jpg格式的图片在解码速度上要比png快.那么我们怎么来 ...

  7. mysql性能优化(二)

    ###> mysql中有一个explain 命令可以用来分析select 语句的运行效果,例如explain可以获得select语句使用的索引情况.排序的情况等等.除此以外,explain 的e ...

  8. Tomcat性能优化(二) ExpiresFilter设置浏览器缓存

    Tomcat性能调优 通过ExpiresFilter设置资源缓存 [官方文档] http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#E ...

  9. Tomcat性能优化(二) 启动参数设置

    一.tomcat绿色版设置方法 进入tomcat/bin目录下,找到catalina.bat文件在文件首行中插入下面这段配置即可. set JAVA_OPTS=-server -Djava.awt.h ...

随机推荐

  1. OpenCV学习(12) 图像的腐蚀与膨胀(3)

    通过使用不同的结构元素来进行膨胀腐蚀操作,可以检测图像中的角点,下面就一步一步看这个算法如果实现角点检测. 原图像: 首先我们创建四个结构元素 先用十字结构元素对原图像进行膨胀操作,得到下面的图像 再 ...

  2. VMware虚拟机的磁盘文件共享给物理机

    启动VMware程序 → 选中某虚拟主机('关闭'状态) → 菜单栏'虚拟机(M)' → '设置(S)' 或直接"编辑虚拟机设置". 2 在“硬件”标签下点击“硬盘(SCSI)”→ ...

  3. QT 异步函数转为同步函数的方法

    在QT中,一般推荐使用异步函数.除了异步函数的非阻塞特性外,QT的Signal/Slot特性在异步函数中可以得到充分的发挥.因此,在QT中,很多API的设计都是使用非阻塞的异步函数作为API,然后执行 ...

  4. Linux0.11内核剖析--内核代码(kernel)--sched.c

    1.概述 linux/kernel/目录下共包括 10 个 C 语言文件和 2 个汇编语言文件以及一个 kernel 下编译文件的管理配置文件 Makefile.其中三个子目录中代码注释的将放在后面的 ...

  5. php内置的http server, 类似于nodejs里面和golang里面的

    原文:https://www.sitepoint.com/taking-advantage-of-phps-built-in-server/ ----------------------------- ...

  6. 利用kettle中的JS来完成ETL数据校验

    最近参与了一个信托行业的BI项目,由于信托业务系统设计的问题,很多都是用户手工录入的数据,也有一些是需要分析的但是用户没有录入的数据,针对这样的数据质量,我们就要在ETL抽取的过程中来对数据流进行校验 ...

  7. win10 mysql57密码重置

    [摘要: 1.my-default.ini 更名my.ini 正在解压的目次上面复造my-default.ini一份更名字为 my.ini. 2.翻开 Windows 情况变量设置, 新建变量名 MY ...

  8. XPath查找节点值示例

    下面一个XML文档,需要找到粗体部分文字: <?xml version='1.0' encoding='utf-8'?> <rep sts="OK" a=&quo ...

  9. 如何使用飞秋FeiQ实现两电脑通信(或传输文件)

    如何使用飞秋FeiQ实现两电脑通信(或传输文件) 1. 在两天电脑上,分别按照飞秋FeiQ 我使用的绿色飞秋2013正式版 2. 使用一根网线,将两电脑的网口连接一起 3. 设置飞秋FeiQ的端口号不 ...

  10. vue - 路由传递参数

    结构目录 1. 页面传值(不同之间的页面传值) 1.1 index.js配置 源码: // 引入vue框架 import Vue from 'vue' // 引入vue-router路由依赖 impo ...