Bagikan melalui


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 OnActivityResultmenunjukkan 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;
}