基于Xam.Plugin.Media创建Xamarin.Forms图片画廊
嘿,Jimmy!我看你正在用Xamarin.Forms开发拍照上传的应用,现在卡在了自定义图片画廊的实现上——无限滚动的2x2网格布局对吧?别担心,我一步步给你捋清楚怎么做,结合你现有的代码来调整。
第一步:改造XAML布局,实现2x2网格画廊
你现在的XAML是用StackLayout展示单张图片,我们需要把它改成ScrollView嵌套带BindableLayout的Grid,这样就能动态绑定图片列表,自动排成2列的网格。
替换你的XAML代码成这样:
<ScrollView Margin="20"> <!-- 保留拍照和同步按钮,放在画廊上方 --> <StackLayout> <StackLayout Orientation="Horizontal" Margin="0,0,0,20"> <Button x:Name="CameraButton" BackgroundColor="#95c241" Text="Foto aufnehmen" TextColor="White" Clicked="CameraButton_Clicked" HorizontalOptions="Start" /> <Button x:Name="SyncButton" BackgroundColor="#95c241" Clicked="SyncButton_Clicked" Text="Synchronisierung" TextColor="White" HorizontalOptions="EndAndExpand" /> </StackLayout> <!-- 核心:2x2网格画廊 --> <Grid x:Name="PhotoGalleryGrid" ColumnDefinitions="*,*" RowSpacing="10" ColumnSpacing="10"> <BindableLayout.ItemTemplate> <DataTemplate> <Image Source="{Binding}" Aspect="AspectFill" HeightRequest="150" /> </DataTemplate> </BindableLayout.ItemTemplate> </Grid> </StackLayout> </ScrollView>
这里的关键是用BindableLayout把Grid和图片数据源绑定,ColumnDefinitions="*,*"会自动把Grid分成宽度均分的两列,图片会自动填充到网格里。
第二步:从数据库读取图片路径,生成图片源集合
你已经把图片路径存在PhotoTable里了,现在需要从数据库取出所有记录,转换成List<ImageSource>,然后绑定到Grid的数据源上。
在你的MainPage.xaml.cs里添加一个加载画廊数据的方法:
private async Task LoadPhotoGalleryAsync() { // 从数据库获取所有图片记录,按拍摄时间倒序排列 var photoRecords = await HelperDB.Instance.conn.Table<PhotoTable>() .OrderByDescending(p => p.Created_At) .ToListAsync(); // 将本地文件路径转换成ImageSource var photoSources = new List<ImageSource>(); foreach (var record in photoRecords) { // 用FromFile比FromStream更适合本地文件,加载更高效 var imageSource = ImageSource.FromFile(record.Filename); photoSources.Add(imageSource); } // 绑定到Grid的数据源 BindableLayout.SetItemsSource(PhotoGalleryGrid, photoSources); }
然后在页面加载时调用这个方法,比如在OnAppearing生命周期里:
protected override async void OnAppearing() { base.OnAppearing(); await LoadPhotoGalleryAsync(); }
另外,拍完照片保存到数据库后,记得刷新画廊,在saveMediaFile方法的最后加上:
await LoadPhotoGalleryAsync();
这样新拍的照片就能立刻显示在画廊里了。
第三步:实现无限滚动(分页加载)
如果你的图片数量很多,一次性加载所有可能会卡顿,这时候可以做分页加载:
- 数据库查询时用
Skip和Take实现分页,比如每次加载20张 - 监听ScrollView的滚动事件,当滚动到接近底部时加载下一页
- 把新的图片源添加到现有集合,而不是替换
示例滚动事件代码:
// 先在XAML的ScrollView上添加Scrolled事件:Scrolled="ScrollView_Scrolled" private int currentPage = 0; private const int pageSize = 20; private async void ScrollView_Scrolled(object sender, ScrolledEventArgs e) { var scrollView = sender as ScrollView; // 判断是否滚动到距离底部100像素以内 var isNearBottom = e.ScrollY + scrollView.Height >= scrollView.ContentSize.Height - 100; if (isNearBottom) { currentPage++; // 加载下一页数据 var nextPagePhotos = await HelperDB.Instance.conn.Table<PhotoTable>() .OrderByDescending(p => p.Created_At) .Skip(currentPage * pageSize) .Take(pageSize) .ToListAsync(); if (nextPagePhotos.Count > 0) { var existingSources = (List<ImageSource>)BindableLayout.GetItemsSource(PhotoGalleryGrid); foreach (var record in nextPagePhotos) { existingSources.Add(ImageSource.FromFile(record.Filename)); } // 通知UI更新数据源 BindableLayout.SetItemsSource(PhotoGalleryGrid, null); BindableLayout.SetItemsSource(PhotoGalleryGrid, existingSources); } } }
第四步:图片加载优化(避免内存溢出)
Xamarin.Forms自带的Image控件加载大量图片可能会导致内存不足,推荐用FFImageLoading第三方库,它支持缓存、压缩、懒加载,非常适合画廊场景。
先安装NuGet包:Xamarin.FFImageLoading和Xamarin.FFImageLoading.Forms
然后把XAML里的Image换成CachedImage:
<ffimageloading:CachedImage Source="{Binding}" Aspect="AspectFill" HeightRequest="150" DownsampleToViewSize="True" CacheDuration="30" />
记得在XAML顶部添加命名空间:
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
最后几点小提示
- 确保你的自定义文件夹路径正确,
ImageSource.FromFile能找到对应的图片文件,可以打印路径到调试窗口验证 - 测试时确认Storage权限已经申请并授予,否则无法读取本地文件
- 如果图片显示异常,检查文件是否被正确保存,路径是否包含特殊字符
这样一步步调整后,你就能实现目标中的2x2无限滚动画廊啦!如果还有细节问题,随时调整就行~
内容的提问来源于stack exchange,提问作者Jimmy Steam

