Sélection d’une photo dans la bibliothèque d’images
Cet article vous montre comment créer une application qui permet à l’utilisateur de sélectionner une photo dans la bibliothèque d’images du téléphone. Comme Xamarin.Forms il n’inclut pas cette fonctionnalité, il est nécessaire d’utiliser DependencyService
pour accéder aux API natives sur chaque plateforme.
Création de l’interface
Tout d’abord, créez une interface dans le code partagé qui expose la fonctionnalité que vous souhaitez implémenter. Dans le cas d’une application de sélection de photo, une seule méthode est requise. Cela est défini dans l’interface IPhotoPickerService
de la bibliothèque .NET Standard de l’exemple de code :
namespace DependencyServiceDemos
{
public interface IPhotoPickerService
{
Task<Stream> GetImageStreamAsync();
}
}
La méthode GetImageStreamAsync
est définie comme asynchrone, car elle doit retourner le résultat rapidement, mais elle ne doit pas retourner d’objet Stream
pour la photo sélectionnée tant que l’utilisateur n’a pas parcouru la bibliothèque d’images et sélectionné une photo.
Cette interface est implémentée dans chaque plateforme à l’aide de code spécifique.
Implémentation iOS
L’implémentation iOS de l’interface IPhotoPickerService
utilise UIImagePickerController
de la manière décrite dans la recette Choisir une photo dans la galerie et l’exemple de code.
L’implémentation iOS est contenue dans la classe PhotoPickerService
dans le projet iOS de l’exemple de code. Pour que cette classe soit visible par le manager DependencyService
, la classe doit être identifiée par un attribut [assembly
] de type Dependency
, être publique et implémenter de manière explicite l’interface IPhotoPickerService
:
[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;
}
...
}
}
La méthode GetImageStreamAsync
crée un UIImagePickerController
et l’initialise pour sélectionner des images dans la photothèque. Deux gestionnaires d’événements sont nécessaires : l’un est utilisé quand l’utilisateur sélectionne une photo et l’autre quand l’utilisateur annule l’affichage de la photothèque. La PresentViewController
méthode affiche ensuite la bibliothèque de photos à l’utilisateur.
À ce stade, la méthode GetImageStreamAsync
doit retourner un objet Task<Stream>
au code qui l’appelle. Cette tâche est exécutée uniquement quand l’utilisateur a fini d’interagir avec la photothèque et que l’un des gestionnaires d’événements est appelé. Dans ces situations, la classe TaskCompletionSource
est essentielle. La classe fournit un objet Task
du type générique approprié à retourner de la méthode GetImageStreamAsync
, et la classe peut être signalée ultérieurement à la fin de la tâche.
Le gestionnaire d’événements FinishedPickingMedia
est appelé quand l’utilisateur a sélectionné une image. Toutefois, le gestionnaire fournit un objet UIImage
et Task
doit retourner un objet Stream
.NET. Cette opération est effectuée en deux étapes : l’objet UIImage
est d’abord converti en fichier PNG ou JPEG en mémoire stocké dans un NSData
objet, puis l’objet NSData
est converti en objet .NET Stream
. Un appel à la méthode SetResult
de l’objet TaskCompletionSource
exécute la tâche en fournissant l’objet 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;
}
}
}
Une application iOS doit être autorisée par l’utilisateur à accéder à la photothèque du téléphone. Ajoutez le code suivant dans la section dict
du fichier Info.plist :
<key>NSPhotoLibraryUsageDescription</key>
<string>Picture Picker uses photo library</string>
Implémentation Android
L’implémentation Android utilise la technique décrite dans la recette Sélectionner une image et l’exemple de code. Toutefois, la méthode appelée quand l’utilisateur a sélectionné une image dans la bibliothèque d’images est une substitution de OnActivityResult
dans une classe dérivée de Activity
. Pour cette raison, la classe MainActivity
normale dans le projet Android a été complétée par un champ, une propriété et une substitution de la méthode OnActivityResult
:
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);
}
}
}
}
La substitution de OnActivityResult
spécifie le fichier de l’image sélectionnée à l’aide d’un objet Uri
Android, mais cet objet peut être converti en objet Stream
.NET en appelant la méthode OpenInputStream
de l’objet ContentResolver
qui a été obtenu à partir de la propriété ContentResolver
de l’activité.
Comme l’implémentation iOS, l’implémentation Android utilise un objet TaskCompletionSource
pour signaler la fin de la tâche. Cet objet TaskCompletionSource
est défini en tant que propriété publique dans la classe MainActivity
. Cela permet de référencer la propriété dans la classe PhotoPickerService
dans le projet Android. Voici la classe avec la méthode 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;
}
}
}
Cette méthode accède à la classe MainActivity
à plusieurs fins : pour la propriété Instance
, pour le champ PickImageId
, pour la propriété TaskCompletionSource
et pour appeler StartActivityForResult
. Cette méthode est définie par la classe FormsAppCompatActivity
, qui est la classe de base de MainActivity
.
Implémentation UWP
Contrairement aux implémentations iOS et Android, l’implémentation du sélecteur de photo pour la plateforme Windows universelle n’utilise pas la classe TaskCompletionSource
. La classe PhotoPickerService
se sert de la classe FileOpenPicker
pour accéder à la photothèque. Étant donné que la méthode PickSingleFileAsync
de FileOpenPicker
est elle-même asynchrone, la méthode GetImageStreamAsync
peut simplement utiliser await
avec cette méthode (et d’autres méthodes asynchrones) et retourner un objet Stream
:
[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();
}
}
}
Implémentation dans le code partagé
Maintenant que l’interface a été implémentée pour chaque plateforme, le code partagé dans la bibliothèque .NET Standard peut l’utiliser.
L’interface utilisateur comprend un Button
sur lequel vous pouvez cliquer pour choisir une photo :
<Button Text="Pick Photo"
Clicked="OnPickPhotoButtonClicked" />
Le gestionnaire d’événements Clicked
utilise la classe DependencyService
pour appeler GetImageStreamAsync
. Il s’ensuit un appel au projet de plateforme. Si la méthode retourne un objet Stream
, le gestionnaire définit la propriété Source
de l’objet image
aux données Stream
:
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;
}