對於前面一篇博客的補充-----顯示大量圓形圖片或頭像時的內存優化做法

一般來說,我們還是比較習慣做成一個控件的,總不可能每次用到圓形圖像的話,去寫上面這麼一大堆。下面我們就

來動手幹!

在 Visual Studio 中新建一個用戶控件(UserControl),我們命名爲 CircleImage。

QQ截圖20150611012146

然後在後臺代碼中定義一個依賴屬性 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 個。

好,走起!

QQ截圖20150611014414

初始運行佔用 40.6 MB 內存,當然這個值可能下次運行就不一樣了,多少會有點波動。

接下來我們加載舊版:

QQ截圖20150611014604

令人喫驚,一瞬間就跑到 250.6 MB 內存了。

那我們再來看看新的版本,記得先將上面這個關掉,重新打開,否則影響結果。

新版:

QQ截圖20150611014634

44.6 MB!基本沒發生任何的變化。可見威力很強大,說明我們的代碼起作用了。


結語:

可以見到這點小小的優化能帶來多大的影響。另外由於 GridView 默認使用了虛擬化,所以實際上並沒有加載到 100

個,但是仍然可以見到舊版的內存佔用十分厲害, 所以根本沒法想象真真正正加載 100 個的時候有多壯觀。小小的

細節可能會引起巨大的變化,考慮到還有衆多 512 MB 內存的 Windows Phone 用戶,這點小小的細節仍然很有必

要去做的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章