Memilih Foto dari Pustaka Gambar
Artikel ini membahas pembuatan aplikasi yang memungkinkan pengguna untuk memilih foto dari pustaka gambar ponsel. Karena Xamarin.Forms tidak termasuk fungsionalitas ini, perlu digunakan DependencyService
untuk mengakses API asli di setiap platform.
Membuat antarmuka
Pertama, buat antarmuka dalam kode bersama yang mengekspresikan fungsionalitas yang diinginkan. Dalam kasus aplikasi pemilihan foto, hanya satu metode yang diperlukan. Ini didefinisikan dalam IPhotoPickerService
antarmuka di pustaka .NET Standard dari kode sampel:
namespace DependencyServiceDemos
{
public interface IPhotoPickerService
{
Task<Stream> GetImageStreamAsync();
}
}
Metode GetImageStreamAsync
ini didefinisikan sebagai asinkron karena metode harus kembali dengan cepat, tetapi tidak dapat mengembalikan Stream
objek untuk foto yang dipilih sampai pengguna telah menelusuri pustaka gambar dan memilihnya.
Antarmuka ini diimplementasikan di semua platform menggunakan kode khusus platform.
Implementasi iOS
Implementasi iOS antarmuka IPhotoPickerService
menggunakan seperti yang UIImagePickerController
dijelaskan dalam Pilih Foto dari resep Galeri dan kode sampel.
Implementasi iOS terkandung dalam PhotoPickerService
kelas dalam proyek iOS dari kode sampel. Untuk membuat kelas ini terlihat oleh DependencyService
manajer, kelas harus diidentifikasi dengan atribut [assembly
] jenis Dependency
, dan kelas harus publik dan secara eksplisit mengimplementasikan IPhotoPickerService
antarmuka:
[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;
}
...
}
}
Metode ini GetImageStreamAsync
membuat UIImagePickerController
dan menginisialisasinya untuk memilih gambar dari pustaka foto. Dua penanganan aktivitas diperlukan: Satu untuk saat pengguna memilih foto dan yang lain saat pengguna membatalkan tampilan pustaka foto. Metode kemudian PresentViewController
menampilkan pustaka foto kepada pengguna.
Pada titik ini, GetImageStreamAsync
metode harus mengembalikan Task<Stream>
objek ke kode yang memanggilnya. Tugas ini selesai hanya ketika pengguna telah selesai berinteraksi dengan pustaka foto dan salah satu penanganan aktivitas dipanggil. Untuk situasi seperti ini, TaskCompletionSource
kelas sangat penting. Kelas menyediakan objek dari jenis generik yang Task
tepat untuk dikembalikan dari GetImageStreamAsync
metode , dan kelas nantinya dapat diberi sinyal ketika tugas selesai.
Penanganan FinishedPickingMedia
aktivitas dipanggil ketika pengguna telah memilih gambar. Namun, handler menyediakan UIImage
objek dan Task
harus mengembalikan objek .NET Stream
. Ini dilakukan dalam dua langkah: Objek UIImage
pertama kali dikonversi ke file PNG atau JPEG dalam memori yang NSData
disimpan dalam objek, lalu NSData
objek dikonversi ke objek .NET Stream
. Panggilan ke SetResult
metode TaskCompletionSource
objek menyelesaikan tugas dengan menyediakan Stream
objek:
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;
}
}
}
Aplikasi iOS memerlukan izin dari pengguna untuk mengakses pustaka foto ponsel. Tambahkan yang berikut ini ke dict
bagian file Info.plist:
<key>NSPhotoLibraryUsageDescription</key>
<string>Picture Picker uses photo library</string>
Implementasi Android
Implementasi Android menggunakan teknik yang dijelaskan dalam resep Pilih Gambar dan kode sampel. Namun, metode yang dipanggil ketika pengguna telah memilih gambar dari pustaka gambar adalah OnActivityResult
penimpaan di kelas yang berasal dari Activity
. Untuk alasan ini, kelas normal MainActivity
dalam proyek Android telah dilengkapi dengan bidang, properti, dan penimpaan OnActivityResult
metode:
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);
}
}
}
}
Penimpaan OnActivityResult
menunjukkan file gambar yang dipilih dengan objek Android Uri
, tetapi ini dapat dikonversi menjadi objek .NET Stream
dengan memanggil OpenInputStream
metode ContentResolver
objek yang diperoleh dari properti aktivitas ContentResolver
.
Seperti implementasi iOS, implementasi Android menggunakan untuk memberi TaskCompletionSource
sinyal ketika tugas telah selesai. Objek ini TaskCompletionSource
didefinisikan sebagai properti publik di MainActivity
kelas . Ini memungkinkan properti untuk dirujuk di PhotoPickerService
kelas di proyek Android. Ini adalah kelas dengan GetImageStreamAsync
metode :
[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;
}
}
}
Metode ini mengakses MainActivity
kelas untuk beberapa tujuan: untuk Instance
properti , untuk PickImageId
bidang , untuk TaskCompletionSource
properti , dan untuk memanggil StartActivityForResult
. Metode ini didefinisikan oleh FormsAppCompatActivity
kelas , yang merupakan kelas dasar dari MainActivity
.
Implementasi UWP
Tidak seperti implementasi iOS dan Android, implementasi pemilih foto untuk Platform Windows Universal tidak memerlukan TaskCompletionSource
kelas . Kelas PhotoPickerService
menggunakan FileOpenPicker
kelas untuk mendapatkan akses ke perpustakaan foto. PickSingleFileAsync
Karena metode FileOpenPicker
itu sendiri asinkron, metode hanya dapat digunakan await
dengan metode itu GetImageStreamAsync
(dan metode asinkron lainnya) dan mengembalikan Stream
objek:
[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();
}
}
}
Menerapkan dalam kode bersama
Sekarang setelah antarmuka diimplementasikan untuk setiap platform, kode bersama di pustaka .NET Standard dapat memanfaatkannya.
UI menyertakan Button
yang dapat diklik untuk memilih foto:
<Button Text="Pick Photo"
Clicked="OnPickPhotoButtonClicked" />
Penanganan Clicked
aktivitas menggunakan DependencyService
kelas untuk memanggil GetImageStreamAsync
. Ini menghasilkan panggilan ke proyek platform. Jika metode mengembalikan Stream
objek, maka handler mengatur Source
properti image
objek ke Stream
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;
}