您的位置:首页 > 其它

对于前面一篇博客的补充-----显示大量圆形图片或头像时的内存优化做法

2015-06-14 21:33 113 查看
一般来说,我们还是比较习惯做成一个控件的,总不可能每次用到圆形图像的话,去写上面这么一大堆。下面我们就

来动手干!

在 Visual Studio 中新建一个用户控件(UserControl),我们命名为 CircleImage。





然后在后台代码中定义一个依赖属性 Source,表示图片的源。由于 BitmapImage 的 UriSource 是 Uri 类型的,因

此我们的 Source 属性也是 Uri 类型。Source 变化时,我们设置到 XAML 中的 BitmapImage 的 UriSource 属性上

去。

public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(Uri), typeof(CircleImage), new PropertyMetadata(null, SourceChanged));

private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var obj = (CircleImage)d;
    var value = (Uri)e.NewValue;
    obj.bitmapImage.UriSource = value;
}

public Uri Source
{
    get
    {
        return (Uri)GetValue(SourceProperty);
    }
    set
    {
        SetValue(SourceProperty, value);
    }
}


然后开始编写前台 XAML:

<UserControl
    x:Class="MyApp.CircleImage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">
    <Ellipse>
        <Ellipse.Fill>
            <ImageBrush>
                <ImageBrush.ImageSource>
                    <BitmapImage x:Name="bitmapImage"
                                 DecodePixelWidth="1"
                                 DecodePixelHeight="1" />
                </ImageBrush.ImageSource>
            </ImageBrush>
        </Ellipse.Fill>
    </Ellipse>
</UserControl>


将 BitmapImage 命名为 bitmapImage,给上面的后台 cs 代码使用。

这里可能你会奇怪,为什么我将解码的大小写成了 1?这是因为,如果解码大小写成小于 1 的整数的话,就等于没有

自动根据渲染大小来解码的功能了,那就跟一开始原文作者的效果一样。所以这里我先写成 1,运行的时候再根据控

件的大小来动态调整。

那么既然要动态调整,那么我们必须得完善后台代码了,添加一些代码上去,修改成这样:

public sealed partial class CircleImage : UserControl
{
    public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source), typeof(Uri), typeof(CircleImage), new PropertyMetadata(null, SourceChanged));

    private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var obj = (CircleImage)d;
        var value = (Uri)e.NewValue;
        obj.bitmapImage.UriSource = value;
    }

    public Uri Source
    {
        get
        {
            return (Uri)GetValue(SourceProperty);
        }
        set
        {
            SetValue(SourceProperty, value);
        }
    }

    public CircleImage()
    {
        this.InitializeComponent();
        this.SizeChanged += CircleImage_SizeChanged;// 监听控件大小发生变化。
    }

    private void ReSize()
    {
        // 计算新的解码大小,向上取整。
        int width = (int)Math.Ceiling(this.ActualWidth);
        int height = (int)Math.Ceiling(this.ActualHeight);

        // 确保解码大小必须大于 0,因为上面的结果可能为 0。
        bitmapImage.DecodePixelWidth = Math.Max(width, 1);
        bitmapImage.DecodePixelHeight = Math.Max(height, 1);

        // 让 BitmapImage 重新渲染。
        var temp = bitmapImage.UriSource;
        bitmapImage.UriSource = null;
        bitmapImage.UriSource = temp;
    }

    private void CircleImage_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ReSize();
    }
}


这样我们就完成了这个控件了,接下来我们来测试下究竟这个东西威力有多大。

测试:

测试图片使用我博客的背景图片,足够大的了,1920*1080,相信应该会吃掉不少内存^-^。

图片地址:http://images.cnblogs.com/cnblogs_com/h82258652/693238/o_wallpaper_summer2013_1920X1080.jpg

测试程序代码:

前台 XAML 代码:

<Page
    x:Class="MyApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" HorizontalAlignment="Center" Orientation="Horizontal">
            <Button Content="加载旧版" Click="BtnOld_Click" />
            <Button Content="加载新版" Click="BtnNew_Click" />
        </StackPanel>
        <GridView Grid.Row="1" x:Name="gvwBigMemory">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Ellipse Width="100" Height="100">
                        <Ellipse.Fill>
                            <ImageBrush ImageSource="http://images.cnblogs.com/cnblogs_com/h82258652/693238/o_wallpaper_summer2013_1920X1080.jpg" />
                        </Ellipse.Fill>
                    </Ellipse>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
        <GridView Grid.Row="2" x:Name="gvwMinMemory">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <local:CircleImage Width="100" Height="100" Source="http://images.cnblogs.com/cnblogs_com/h82258652/693238/o_wallpaper_summer2013_1920X1080.jpg" />
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>
</Page>


都放在 GridView 里面,大小都设定为 100*100。

后台代码:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private void BtnOld_Click(object sender, RoutedEventArgs e)
    {
        gvwBigMemory.ItemsSource = Enumerable.Range(1, 100);
    }

    private void BtnNew_Click(object sender, RoutedEventArgs e)
    {
        gvwMinMemory.ItemsSource = Enumerable.Range(1, 100);
    }
}


很简单,就是让 GridView 加载 100 个。

好,走起!





初始运行占用 40.6 MB 内存,当然这个值可能下次运行就不一样了,多少会有点波动。

接下来我们加载旧版:





令人吃惊,一瞬间就跑到 250.6 MB 内存了。

那我们再来看看新的版本,记得先将上面这个关掉,重新打开,否则影响结果。

新版:





44.6 MB!基本没发生任何的变化。可见威力很强大,说明我们的代码起作用了。

结语:

可以见到这点小小的优化能带来多大的影响。另外由于 GridView 默认使用了虚拟化,所以实际上并没有加载到 100

个,但是仍然可以见到旧版的内存占用十分厉害, 所以根本没法想象真真正正加载 100 个的时候有多壮观。小小的

细节可能会引起巨大的变化,考虑到还有众多 512 MB 内存的 Windows Phone 用户,这点小小的细节仍然很有必

要去做的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: