Windows Phone 性能优化(二)_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > Windows Phone 性能优化(二)

Windows Phone 性能优化(二)

 2013/11/6 16:10:51  段博琼  博客园  我要评论(0)
  • 摘要:这篇文章的demo是在(一)的基础上进行的调整,逻辑基本相似。本文只列和上一篇出不同的代码。为了实现自定义的虚拟化,把上一篇文章的ListBox换成ScrollViewer+ItemsControl,这样组合在实际的项目中又是还是会用到的,比如,如果我们需要对ScrollViewer进行很多的控制,比如获取它的“滑动”事件,ScrollViewer中在放置其它控件,或者直接定制它的样式等等
  • 标签:Windows 性能优化 优化

 

这篇文章的 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 = 0;
            for (int i = 0; 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高度
                height_sum = height_sum + sp.ActualHeight + 30;

                // 设置它的高度为自己的实际高度
                sp.Height = sp.ActualHeight; 
            }

            // 每0.5秒钟,循环检查一次列表中,哪些项在屏幕内,如果在屏幕内,则显示,如果
            // 在屏幕外,则隐藏
            Observable.Interval(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher().Subscribe((_) =>
                {
                    foreach (var keyValue in dic)
                    {
                        if (((scrollViewer.VerticalOffset - 500) > keyValue.Key || keyValue.Key > (scrollViewer.VerticalOffset + 900)))
                        {
                            keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Collapsed;
                            keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Collapsed;
                        }
                        else
                        {

                            keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Visible;
                            keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Visible;
                        }
                    }
                });
        }
        
        // 查找“视图树”中的控件
        private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
        {
            var count = VisualTreeHelper.GetChildrenCount(parentElement);
            if (count == 0) return null;

            for (int i = 0; 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,为了直观就没有再优化。比如每次遍历的时间,

屏幕的可视区域等。

 

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

如果还在屏幕外,则跳过,等等。关于如何优化上面算法这里就不在多讲了。

 

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

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

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

更加方便。

 

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

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

 

 

引申

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

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

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

 

 

本文工程 demo 下载

发表评论
用户名: 匿名