简体   繁体   中英

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). 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. 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.

Previously I've tried using the Media.Plugin but since November '20 the lib is dismissed.

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:

Xamarin.Essentials 1.6 introduced official support for picking/taking photos and videos with the new Media Picker API.

According to your description, if you want to limit selected photos numbers, I suggest you can check the numbers in 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.

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.

[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.

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.

 <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();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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