[英]How to implement the classic media picker in Xamarin.Forms?
我正在開發這個 xamarin.forms 應用程序,該應用程序要求用戶從其設備中 select 一定數量的照片(從最少 1 張照片到指定的最大數量甚至無限)。 我一直在努力了解如何實現它。 我使用了 Microsoft 文檔中建議的FilePicker ,但這樣我就無法限制可選擇的圖像數量。 我也嘗試使用依賴服務在兩個項目中實現圖庫,但我認為我走錯了路:在 android 上,我在某些文件夾中搜索設備圖像,但是這樣,如果用戶將其照片存儲在外部應用程序(例如谷歌照片或亞馬遜照片)他看不到(並選擇)它們,還沒有在 iOS 上嘗試過。
以前我曾嘗試使用Media.Plugin但自 20 年 11 月以來,該庫已被取消。
我不明白如何正確實現此代碼,以及考慮到它在應用程序中如此常見的事實,為什么沒有它的視圖。
如何才能做到這一點?
如 Media.Plugin 所述:
Xamarin.Essentials 1.6 引入了對使用新媒體選擇器 API 選擇/拍攝照片和視頻的官方支持。
根據您的描述,如果您想限制選擇的照片數量,我建議您可以查看 OnActivityResult 中的數字。 我做了一個樣本,你可以看看:
首先,在.net標准項目中創建依賴服務打開圖庫的接口。
public interface IMediaService
{
Task OpenGallery();
}
其次,在 Android 項目中,我創建了一個服務來處理照片庫的打開,我也在使用 Permission 插件來檢查和請求 Android 6 及更高版本中的權限。
[assembly: Xamarin.Forms.Dependency(typeof(MediaService))]
namespace pickerimage.Droid
{
public class MediaService : Java.Lang.Object, IMediaService
{
public async Task OpenGallery()
{
try
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Plugin.Permissions.Abstractions.Permission.Storage);
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Plugin.Permissions.Abstractions.Permission.Storage))
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "Need Storage permission to access to your photos.", ToastLength.Long).Show();
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Plugin.Permissions.Abstractions.Permission.Storage });
status = results[Plugin.Permissions.Abstractions.Permission.Storage];
}
if (status == PermissionStatus.Granted)
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "Select max 20 images", ToastLength.Long).Show();
var imageIntent = new Intent(
Intent.ActionPick);
imageIntent.SetType("image/*");
imageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
imageIntent.SetAction(Intent.ActionGetContent);
((Activity)Forms.Context).StartActivityForResult(
Intent.CreateChooser(imageIntent, "Select photo"), 1);
}
else if (status != PermissionStatus.Unknown)
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "Permission Denied. Can not continue, try again.", ToastLength.Long).Show();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Toast.MakeText(Xamarin.Forms.Forms.Context, "Error. Can not continue, try again.", ToastLength.Long).Show();
}
}
}
}
public static class ImageHelpers
{
public static string SaveFile(string collectionName, byte[] imageByte, string fileName)
{
var fileDir = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures), collectionName);
if (!fileDir.Exists())
{
fileDir.Mkdirs();
}
var file = new Java.IO.File(fileDir, fileName);
System.IO.File.WriteAllBytes(file.Path, imageByte);
return file.Path;
}
public static byte[] RotateImage(string path)
{
byte[] imageBytes;
var originalImage = BitmapFactory.DecodeFile(path);
var rotation = GetRotation(path);
var width = (originalImage.Width * 0.25);
var height = (originalImage.Height * 0.25);
var scaledImage = Bitmap.CreateScaledBitmap(originalImage, (int)width, (int)height, true);
Bitmap rotatedImage = scaledImage;
if (rotation != 0)
{
var matrix = new Matrix();
matrix.PostRotate(rotation);
rotatedImage = Bitmap.CreateBitmap(scaledImage, 0, 0, scaledImage.Width, scaledImage.Height, matrix, true);
scaledImage.Recycle();
scaledImage.Dispose();
}
using (var ms = new MemoryStream())
{
rotatedImage.Compress(Bitmap.CompressFormat.Jpeg, 90, ms);
imageBytes = ms.ToArray();
}
originalImage.Recycle();
rotatedImage.Recycle();
originalImage.Dispose();
rotatedImage.Dispose();
GC.Collect();
return imageBytes;
}
private static int GetRotation(string filePath)
{
using (var ei = new ExifInterface(filePath))
{
var orientation = (Android.Media.Orientation)ei.GetAttributeInt(ExifInterface.TagOrientation, (int)Android.Media.Orientation.Normal);
switch (orientation)
{
case Android.Media.Orientation.Rotate90:
return 90;
case Android.Media.Orientation.Rotate180:
return 180;
case Android.Media.Orientation.Rotate270:
return 270;
default:
return 0;
}
}
}
}
}
最后,在 MainActivity.cs 中,檢查clipData.ItemCount
計數,如果計數大於 20,則返回。
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 1 && resultCode == Result.Ok)
{
List<string> images = new List<string>();
if (data != null)
{
ClipData clipData = data.ClipData;
if (clipData != null)
{
if(clipData.ItemCount>20)
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "please select max 20 images", ToastLength.Long).Show();
return;
}
for (int i = 0; i < clipData.ItemCount; i++)
{
ClipData.Item item = clipData.GetItemAt(i);
Android.Net.Uri uri = item.Uri;
var path = GetRealPathFromURI(uri);
if (path != null)
{
//Rotate Image
var imageRotated = ImageHelpers.RotateImage(path);
var newPath = ImageHelpers.SaveFile("TmpPictures", imageRotated, System.DateTime.Now.ToString("yyyyMMddHHmmssfff"));
images.Add(newPath);
}
}
}
else
{
Android.Net.Uri uri = data.Data;
var path = GetRealPathFromURI(uri);
if (path != null)
{
//Rotate Image
var imageRotated = ImageHelpers.RotateImage(path);
var newPath = ImageHelpers.SaveFile("TmpPictures", imageRotated, System.DateTime.Now.ToString("yyyyMMddHHmmssfff"));
images.Add(newPath);
}
}
MessagingCenter.Send<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelected", images);
}
}
}
public String GetRealPathFromURI(Android.Net.Uri contentURI)
{
try
{
ICursor imageCursor = null;
string fullPathToImage = "";
imageCursor = ContentResolver.Query(contentURI, null, null, null, null);
imageCursor.MoveToFirst();
int idx = imageCursor.GetColumnIndex(MediaStore.Images.ImageColumns.Data);
if (idx != -1)
{
fullPathToImage = imageCursor.GetString(idx);
}
else
{
ICursor cursor = null;
var docID = DocumentsContract.GetDocumentId(contentURI);
var id = docID.Split(':')[1];
var whereSelect = MediaStore.Images.ImageColumns.Id + "=?";
var projections = new string[] { MediaStore.Images.ImageColumns.Data };
cursor = ContentResolver.Query(MediaStore.Images.Media.InternalContentUri, projections, whereSelect, new string[] { id }, null);
if (cursor.Count == 0)
{
cursor = ContentResolver.Query(MediaStore.Images.Media.ExternalContentUri, projections, whereSelect, new string[] { id }, null);
}
var colData = cursor.GetColumnIndexOrThrow(MediaStore.Images.ImageColumns.Data);
cursor.MoveToFirst();
fullPathToImage = cursor.GetString(colData);
}
return fullPathToImage;
}
catch (Exception ex)
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "Unable to get path", ToastLength.Long).Show();
}
return null;
}
}
在 MainPage 中顯示圖像。
<StackLayout>
<Button
BackgroundColor="Blue"
Clicked="Handle_Clicked"
Text="Select From Gallery"
TextColor="White" />
<ListView x:Name="listItems">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image HeightRequest="100" Source="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class MainPage : ContentPage
{
List<string> _images = new List<string>();
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelected", (s, images) =>
{
listItems.ItemsSource = images;
_images = images;
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<App, List<string>>(this, "ImagesSelected");
}
async void Handle_Clicked(object sender, System.EventArgs e)
{
await DependencyService.Get<IMediaService>().OpenGallery();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.