gpt4 book ai didi

c# - Windows Phone 8.1 - 在 gridview 上使用图片库图像进行数据虚拟化

转载 作者:行者123 更新时间:2023-11-30 20:23:46 25 4
gpt4 key购买 nike

我正在构建一个 Windows Phone 8.1 应用程序,它要求我在 GridView 中显示图片库中的所有图像。我已经构建了一个名为 VirtualList 的类,它是一个支持增量加载的列表,并且我已将图片库中的所有图像添加到该列表中。当图像数量减少(少于 80 张照片)时,一切正常,但当超过 80 张照片时,应用程序会因 OutOfMemoryException 而关闭。我想目前没有显示的项目没有保存在内存中,或者是吗?就我的目的而言,我应该继续使用增量加载,还是应该切换到随机访问数据虚拟化?如果我应该切换到随机访问数据虚拟化,您能否提供一个有关如何实现它的示例?

我的代码如下:

虚拟列表.cs

class VirtualList : List<Windows.UI.Xaml.Media.ImageSource>, ISupportIncrementalLoading
{

private IReadOnlyList<StorageFile> photos;

public VirtualList(IReadOnlyList<StorageFile> files) : base()
{
photos = files;
}

public bool HasMoreItems
{
get
{
return this.Count < photos.Count;
}
}

public Windows.Foundation.IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return LoadMoreItemsAwaitable(count).AsAsyncOperation<LoadMoreItemsResult>();
}

private async Task<LoadMoreItemsResult> LoadMoreItemsAwaitable(uint count)
{
for (int i = Count; i < Count + count; i++)
{
using (var fileStream = await photos[i].OpenAsync(FileAccessMode.Read))
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
this.Add(bitmapImage);
}
}

return new LoadMoreItemsResult { Count = count };
}
}

XAML 代码 (MainPage.xaml):

<GridView x:Name="photosGrid" Height="392" Width="400" ItemsSource="{Binding}" Margin="0,0,-0.333,0" SelectionMode="Multiple" Background="Black">
<GridView.ItemTemplate>
<DataTemplate>
<Image Width="90" Height="90" Margin="5" Source="{Binding}" Stretch="UniformToFill"/>
</DataTemplate>
</GridView.ItemTemplate>

</GridView>

MainPage.xaml.cs代码

//This code is inside OnNavigatedTo method
var files = await KnownFolders.CameraRoll.GetFilesAsync();
VirtualList imageList = new VirtualList(files);
photosGrid.DataContext = imageList;

最佳答案

这里有很多问题。

第一个是你的收藏需要实现INotifyPropertyChanged正确支持增量加载(不要问我为什么)。幸运的是它很容易修复:只需继承自 ObservableCollection<T>而不是 List<T> .

第二个问题来自您对 LoadMoreItemsAwaitable 的实现.更具体地说,for循环:

for (int i = Count; i < Count + count; i++)
{
using (var fileStream = await photos[i].OpenAsync(FileAccessMode.Read))
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
this.Add(bitmapImage);
}
}

每次您将一个项目添加到集合 ( this.Add(bitmapImage) ) 时,Count 的值增加。结果是 iCount两者同时增加,使您的循环无限。为防止出现这种情况,请保存 Count 的值循环外:

int offset = this.Count;

for (int i = offset; i < offset + count && i < photos.Count; i++)
{
using (var fileStream = await photos[i].OpenAsync(FileAccessMode.Read))
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
this.Add(bitmapImage);
}
}

请注意,我还检查了 i低于photos.Count ,否则您可能会遇到 ArgumentOufOfRangeException。

从现在开始,您可以尝试一下,您会发现它会奏效。不过,如果您向下滚动列表,内存会不断增加。为什么?因为你正在存储你的 BitmapImage ,从而抵消了虚拟化的好处。

让我解释一下:

  1. 首先,您的网格显示,比方说,五个元素。因此您的集合中加载了五个元素。
  2. 用户向下滚动,网格需要显示接下来的五个元素。它从您的集合中加载五个新元素,并丢弃之前的五个元素以节省内存(由于虚拟化)。问题:仍然将这五件元素存储在您的收藏中!因此,内存永远不会被释放。

不幸的是,我认为没有完美的方法可以用 ISupportIncrementalLoading 解决这个问题(没有 API 告诉网格需要重新显示以前的元素,因此您需要始终保留它们)。但是您仍然可以通过仅存储文件路径而不是 BitmapImage 来避免占用内存。 .

问题:有一种方法可以填充 Image通过仅提供路径(通过使用 ms-appx URI 方案)进行控制,但据我所知,它不适用于图片库中存储的图片。所以你确实需要返回BitmapImage在某些时候控制。起初,我考虑编写一个转换器(将路径转换为 ​​BitmapImage ,但它需要异步 API,并且转换器是同步的......我能想到的最简单的解决方案是制作自己的 Image 控件,即可以加载这种路径。Image 控件是密封的,因此您不能直接从它继承(有时,我认为 WinRT 是专门为惹恼开发人员而设计的),但您可以将其包装在 UserControl 中。

让我们创建一个名为 LocalImage 的用户控件. XAML 只是包装了 Image控制:

<UserControl
x:Class="StackOverflowUniversal10.LocalImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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">

<Image x:Name="Image" Width="90" Height="90" Margin="5" Source="{Binding}" Stretch="UniformToFill"/>
</UserControl>

在代码隐藏中,我们创建一个依赖属性,并使用它来加载图片:

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

public LocalImage()
{
this.InitializeComponent();
}

public string Source
{
get { return this.GetValue(SourceProperty) as string; }
set { this.SetValue(SourceProperty, value); }
}

private async static void SourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var control = (LocalImage)obj;

var path = e.NewValue as string;

if (string.IsNullOrEmpty(path))
{
control.Image.Source = null;
}
else
{
var file = await StorageFile.GetFileFromPathAsync(path);

using (var fileStream = await file.OpenAsync(FileAccessMode.Read))
{
BitmapImage bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(fileStream);
control.Image.Source = bitmapImage;
}
}
}
}

然后修改您的页面以使用 UserControl 而不是 Image控制:

<GridView x:Name="photosGrid" Height="382" Width="400" ItemsSource="{Binding}" Margin="0,0,-0.333,0" SelectionMode="Multiple" Background="Black">
<GridView.ItemTemplate>
<DataTemplate>
<local:LocalImage Width="90" Height="90" Margin="5" Source="{Binding}"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>

最后但同样重要的是,更改您的收藏以存储路径而不是图片:

class VirtualList : ObservableCollection<string>, ISupportIncrementalLoading
{
private IReadOnlyList<StorageFile> photos;

public VirtualList(IReadOnlyList<StorageFile> files) : base()
{
photos = files;
}

public bool HasMoreItems
{
get
{
return this.Count < photos.Count;
}
}

public Windows.Foundation.IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return LoadMoreItemsAwaitable(count).AsAsyncOperation<LoadMoreItemsResult>();
}

private async Task<LoadMoreItemsResult> LoadMoreItemsAwaitable(uint count)
{
int offset = this.Count;

for (int i = offset; i < offset + count && i < photos.Count; i++)
{
this.Add(photos[i].Path);
}

return new LoadMoreItemsResult { Count = count };
}
}

它应该可以工作,内存消耗稳定。请注意,您可以(并且应该)通过设置 DecodePixelHeight 进一步减少内存消耗。和 DecodePixelWidth你的属性 BitmapImage (这样运行时将在内存中加载缩略图而不是全分辨率图片)。

关于c# - Windows Phone 8.1 - 在 gridview 上使用图片库图像进行数据虚拟化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28087721/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com