Sdílet prostřednictvím


Výběr fotky z knihovny obrázků

Tento článek vás provede vytvořením aplikace, která uživateli umožňuje vybrat fotku z knihovny obrázků telefonu. Vzhledem k tomu Xamarin.Forms , že tato funkce nezahrnuje, je nutné použít DependencyService přístup k nativním rozhraním API na každé platformě.

Vytvoření rozhraní

Nejprve vytvořte rozhraní ve sdíleném kódu, které vyjadřuje požadovanou funkci. V případě aplikace pro výběr fotek se vyžaduje jenom jedna metoda. Toto je definováno v rozhraní v IPhotoPickerService knihovně .NET Standard ukázkového kódu:

namespace DependencyServiceDemos
{
    public interface IPhotoPickerService
    {
        Task<Stream> GetImageStreamAsync();
    }
}

Metoda GetImageStreamAsync je definována jako asynchronní, protože metoda se musí rychle vrátit, ale nemůže vrátit objekt pro vybranou Stream fotografii, dokud uživatel neprojde knihovnu obrázků a vybere ji.

Toto rozhraní se implementuje ve všech platformách pomocí kódu specifického pro platformu.

Implementace iOS

Implementace rozhraní pro IPhotoPickerService iOS používá metodu UIImagePickerController popsanou v části Zvolit fotku z receptu galerie a vzorového kódu.

Implementace iOS je obsažena ve PhotoPickerService třídě v projektu iOS vzorového kódu. Aby byla tato třída viditelná DependencyService pro správce, musí být třída identifikována atributem [assembly] typu Dependencya třída musí být veřejná a explicitně implementovat IPhotoPickerService rozhraní:

[assembly: Dependency (typeof (PhotoPickerService))]
namespace DependencyServiceDemos.iOS
{
    public class PhotoPickerService : IPhotoPickerService
    {
        TaskCompletionSource<Stream> taskCompletionSource;
        UIImagePickerController imagePicker;

        public Task<Stream> GetImageStreamAsync()
        {
            // Create and define UIImagePickerController
            imagePicker = new UIImagePickerController
            {
                SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
                MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
            };

            // Set event handlers
            imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
            imagePicker.Canceled += OnImagePickerCancelled;

            // Present UIImagePickerController;
            UIWindow window = UIApplication.SharedApplication.KeyWindow;
            var viewController = window.RootViewController;
            viewController.PresentViewController(imagePicker, true, null);

            // Return Task object
            taskCompletionSource = new TaskCompletionSource<Stream>();
            return taskCompletionSource.Task;
        }
        ...
    }
}

Metoda GetImageStreamAsync vytvoří UIImagePickerController a inicializuje ji pro výběr obrázků z knihovny fotek. Jsou vyžadovány dva obslužné rutiny událostí: Jeden pro, když uživatel vybere fotku a druhý pro případ, kdy uživatel zruší zobrazení knihovny fotografií. Metoda PresentViewController pak uživateli zobrazí knihovnu fotografií.

V tomto okamžiku GetImageStreamAsync musí metoda vrátit Task<Stream> objekt do kódu, který ho volá. Tato úloha je dokončena pouze v případě, že uživatel dokončil interakci s knihovnou fotografií a volá se jeden z obslužných rutin událostí. V takových situacích TaskCompletionSource je třída nezbytná. Třída poskytuje Task objekt správného obecného typu, který se má vrátit z GetImageStreamAsync metody, a třída lze později signalizovat při dokončení úkolu.

Obslužná rutina FinishedPickingMedia události se volá, když uživatel vybral obrázek. Obslužná rutina však poskytuje UIImage objekt a Task musí vrátit objekt .NET Stream . To se provádí ve dvou krocích: Objekt UIImage se nejprve převede na soubor VE formátu PNG nebo JPEG uložený v NSData objektu a pak NSData se objekt převede na objekt .NET Stream . Volání SetResult metody objektu TaskCompletionSource dokončí úlohu poskytnutím objektu Stream :

namespace DependencyServiceDemos.iOS
{
    public class PhotoPickerService : IPhotoPickerService
    {
        TaskCompletionSource<Stream> taskCompletionSource;
        UIImagePickerController imagePicker;
        ...
        void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
        {
            UIImage image = args.EditedImage ?? args.OriginalImage;

            if (image != null)
            {
                // Convert UIImage to .NET Stream object
                NSData data;
                if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
                {
                    data = image.AsPNG();
                }
                else
                {
                    data = image.AsJPEG(1);
                }
                Stream stream = data.AsStream();

                UnregisterEventHandlers();

                // Set the Stream as the completion of the Task
                taskCompletionSource.SetResult(stream);
            }
            else
            {
                UnregisterEventHandlers();
                taskCompletionSource.SetResult(null);
            }
            imagePicker.DismissModalViewController(true);
        }

        void OnImagePickerCancelled(object sender, EventArgs args)
        {
            UnregisterEventHandlers();
            taskCompletionSource.SetResult(null);
            imagePicker.DismissModalViewController(true);
        }

        void UnregisterEventHandlers()
        {
            imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
            imagePicker.Canceled -= OnImagePickerCancelled;
        }
    }
}

Aplikace pro iOS vyžaduje od uživatele oprávnění pro přístup ke knihovně fotek telefonu. Do části souboru Info.plist přidejte následující dict položky:

<key>NSPhotoLibraryUsageDescription</key>
<string>Picture Picker uses photo library</string>

Implementace Androidu

Implementace Androidu používá techniku popsanou v receptu vybrat obrázek a vzorový kód. Metoda, která je volána, když uživatel vybral obrázek z knihovny obrázků je OnActivityResult přepsání ve třídě, která je odvozena z Activity. Z tohoto důvodu byla normální MainActivity třída v projektu Android doplněna polem, vlastností a přepsání OnActivityResult metody:

public class MainActivity : FormsAppCompatActivity
{
    internal static MainActivity Instance { get; private set; }  

    protected override void OnCreate(Bundle savedInstanceState)
    {
        // ...
        Instance = this;
    }
    // ...
    // Field, property, and method for Picture Picker
    public static readonly int PickImageId = 1000;

    public TaskCompletionSource<Stream> PickImageTaskCompletionSource { set; get; }

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
    {
        base.OnActivityResult(requestCode, resultCode, intent);

        if (requestCode == PickImageId)
        {
            if ((resultCode == Result.Ok) && (intent != null))
            {
                Android.Net.Uri uri = intent.Data;
                Stream stream = ContentResolver.OpenInputStream(uri);

                // Set the Stream as the completion of the Task
                PickImageTaskCompletionSource.SetResult(stream);
            }
            else
            {
                PickImageTaskCompletionSource.SetResult(null);
            }
        }
    }
}

Přepsání OnActivityResultoznačuje vybraný soubor obrázku s objektem Androidu Uri , ale lze jej převést na objekt .NET Stream voláním OpenInputStream metody objektu ContentResolver , který byl získán z vlastnosti aktivity ContentResolver .

Stejně jako implementace iOS používá TaskCompletionSource implementace Androidu signál, když je úkol dokončen. Tento TaskCompletionSource objekt je definován jako veřejná vlastnost ve MainActivity třídě. To umožňuje odkazovat na vlastnost ve PhotoPickerService třídě v projektu Android. Toto je třída s metodou GetImageStreamAsync :

[assembly: Dependency(typeof(PhotoPickerService))]
namespace DependencyServiceDemos.Droid
{
    public class PhotoPickerService : IPhotoPickerService
    {
        public Task<Stream> GetImageStreamAsync()
        {
            // Define the Intent for getting images
            Intent intent = new Intent();
            intent.SetType("image/*");
            intent.SetAction(Intent.ActionGetContent);

            // Start the picture-picker activity (resumes in MainActivity.cs)
            MainActivity.Instance.StartActivityForResult(
                Intent.CreateChooser(intent, "Select Picture"),
                MainActivity.PickImageId);

            // Save the TaskCompletionSource object as a MainActivity property
            MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Stream>();

            // Return Task object
            return MainActivity.Instance.PickImageTaskCompletionSource.Task;
        }
    }
}

Tato metoda přistupuje ke MainActivity třídě pro několik účelů: pro Instance vlastnost, pro PickImageId pole, pro TaskCompletionSource vlastnost a volání StartActivityForResult. Tato metoda je definována FormsAppCompatActivity třídou, což je základní třída MainActivity.

Implementace UPW

Na rozdíl od implementací pro iOS a Android nevyžaduje TaskCompletionSource implementace výběru fotografií pro Univerzální platforma Windows třídu. Třída PhotoPickerService používá FileOpenPicker třídu k získání přístupu ke knihovně fotografií. Vzhledem k tomu, že PickSingleFileAsync metoda FileOpenPicker je sama o sobě asynchronní, GetImageStreamAsync může metoda jednoduše použít await s danou Stream metodou (a další asynchronní metody) a vrátit objekt:

[assembly: Dependency(typeof(PhotoPickerService))]
namespace DependencyServiceDemos.UWP
{
    public class PhotoPickerService : IPhotoPickerService
    {
        public async Task<Stream> GetImageStreamAsync()
        {
            // Create and initialize the FileOpenPicker
            FileOpenPicker openPicker = new FileOpenPicker
            {
                ViewMode = PickerViewMode.Thumbnail,
                SuggestedStartLocation = PickerLocationId.PicturesLibrary,
            };

            openPicker.FileTypeFilter.Add(".jpg");
            openPicker.FileTypeFilter.Add(".jpeg");
            openPicker.FileTypeFilter.Add(".png");

            // Get a file and return a Stream
            StorageFile storageFile = await openPicker.PickSingleFileAsync();

            if (storageFile == null)
            {
                return null;
            }

            IRandomAccessStreamWithContentType raStream = await storageFile.OpenReadAsync();
            return raStream.AsStreamForRead();
        }
    }
}

Implementace ve sdíleném kódu

Teď, když je rozhraní implementované pro každou platformu, může sdílený kód v knihovně .NET Standard využít výhod.

Uživatelské rozhraní obsahuje fotku Button , na kterou můžete kliknout:

<Button Text="Pick Photo"
        Clicked="OnPickPhotoButtonClicked" />

Obslužná rutina Clicked události používá DependencyService třídu k volání GetImageStreamAsync. Výsledkem je volání projektu platformy. Pokud metoda vrátí Stream objekt, obslužná rutina nastaví Source vlastnost image objektu Stream na data:

async void OnPickPhotoButtonClicked(object sender, EventArgs e)
{
    (sender as Button).IsEnabled = false;

    Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
    if (stream != null)
    {
        image.Source = ImageSource.FromStream(() => stream);
    }

    (sender as Button).IsEnabled = true;
}