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 Dependency
a 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í OnActivityResult
označ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;
}