简体   繁体   English

如何实现Xamarin.Forms中的经典媒体选择器?

[英]How to implement the classic media picker in Xamarin.Forms?

I'm working on this xamarin.forms app that requires the user to select a certain number of photos from its device (from a minimum of 1 photo to a specified maximum number or even limitless).我正在开发这个 xamarin.forms 应用程序,该应用程序要求用户从其设备中 select 一定数量的照片(从最少 1 张照片到指定的最大数量甚至无限)。 I've struggled a bit getting to know how to implement that.我一直在努力了解如何实现它。 I've used the FilePicker suggested in Microsoft docs but this way I can't limit the selectable image number.我使用了 Microsoft 文档中建议的FilePicker ,但这样我就无法限制可选择的图像数量。 I've also tried to use dependency services to implement a gallery in both projects but I think I'm on the wrong path: on android I search the device images in certain folders but this way, if the user has its photos stored in an external app (such as Google photos or Amazon photos) he can't see (and select) them, haven't tried on iOS yet.我也尝试使用依赖服务在两个项目中实现图库,但我认为我走错了路:在 android 上,我在某些文件夹中搜索设备图像,但是这样,如果用户将其照片存储在外部应用程序(例如谷歌照片或亚马逊照片)他看不到(并选择)它们,还没有在 iOS 上尝试过。

Previously I've tried using the Media.Plugin but since November '20 the lib is dismissed.以前我曾尝试使用Media.Plugin但自 20 年 11 月以来,该库已被取消。

I don't understand how this code can be implemented correctly, and why there isn't a view for it given the fact that it's so common in apps.我不明白如何正确实现此代码,以及考虑到它在应用程序中如此常见的事实,为什么没有它的视图。

How can this be done?如何才能做到这一点?

As described on Media.Plugin:如 Media.Plugin 所述:

Xamarin.Essentials 1.6 introduced official support for picking/taking photos and videos with the new Media Picker API. Xamarin.Essentials 1.6 引入了对使用新媒体选择器 API 选择/拍摄照片和视频的官方支持。

According to your description, if you want to limit selected photos numbers, I suggest you can check the numbers in OnActivityResult.根据您的描述,如果您想限制选择的照片数量,我建议您可以查看 OnActivityResult 中的数字。 I do one sample that you can take a look:我做了一个样本,你可以看看:

Firstly, create interface for the dependency service to open the gallery in .net standard project.首先,在.net标准项目中创建依赖服务打开图库的接口。

public interface IMediaService
{
    Task OpenGallery();
}

Secondly, in the Android project, I created a service to handle the photo gallery opening, also I'm using the Permission plugin for check and request permissions in Android 6 and higher.其次,在 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;
            }
        }
    }
}
 }

Finally, in MainActivity.cs, check clipData.ItemCount count, if count is greather than 20, return.最后,在 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;

    }
}

Display image in MainPage.在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM