記事一覧

イメージギャラリーを表示して写真を取得する方法 | Xamarin.Forms


今回はXamarin.Formsでイメージギャラリーを画面に表示する方法をご紹介いたします。イメージギャラリー(写真選択画面)はネイティブコードでのサンプルがネット上にありますが、iOSとAndroidでコードや形式が異なるため、PCLで記述しにくいと思っていましたが、DependencyServiceで比較的容易に記述ができましたので、以下に記載したいと思います。
尚、CustomRendererを作成してXAML描画時の処理も実現できましたが、DependencySerivceの方が敷居が低いと感じました。





イメージギャラリー(イメージピッカー)の表示形式
iOS9
xamarin_imageGallery_01.png
Android5
xamarin_imageGallery_02.png


前提条件
・Windows10
・Visual Studio 2015 Community Update3
・Xamarin 4.3.0.784 (NuGet Xamarin.Forms 2.3.4.224)
・macOS Sierra 10.12.4 / Xcode8.3.1 / Xamarin.iOS 10.4.0.123


1.PCLの記述

iOSとAndroidで共通の記述となります。
IImageService.cs
public interface IImageService
{
    void ShowImageGallery();
}

iOSとAndroidで選択された写真を実際のページのImageコントロールのソースとして読み込むコードが以下の通りです。
App.cs
using Xamarin.Forms;
public partial class App : Application
{
    private static Image _pickupImage;
    private static ContentPage _resultPage;
    public static ContentPage ResultPage {
        set
        {
            _resultPage = value;
        }
    }
    public static ImageSource GetPickupImageSource()
    {
        return _pickupImage.Source;
    }
    public static void SetPickupImageSource(string filePath)
    {
        //写真を表示する対象のページが設定されていない場合は処理を中断
        if (_resultPage == null)
        {
            return;
        }
        _pickupImage = _resultPage.FindByName<Image>("imgPicture");
        _pickupImage.Source = ImageSource.FromFile(filePath);
    }
    public static void SetPickupImageSource(ImageSource source)
    {
        //写真を表示する対象のページが設定されていない場合は処理を中断
        if (_resultPage == null)
        {
            return;
        }
        _pickupImage = _resultPage.FindByName<Image>("imgPicture");
        _pickupImage.Source = source;
    }
    public App()
    {
        MainPage = new NavigationPage(new TestPage());
    }
}


2.iOSの場合

iOSのページ上にイメージピッカーを表示します。
ImageService.cs
[assembly: Dependency(typeof(ImageService))]
public class ImageService : IImageService
{
    /// <summary>
    /// イメージ選択画面を表示する(外部アプリ)
    /// </summary>
    public void ShowImageGallery()
    {
        UIViewController viewController;
        UINavigationController navigationController;
                       
        viewController = new ContactFriends.iOS.Models.ImagePickerController();
        navigationController = this.FindNavigationController();
        navigationController.PresentViewController(viewController, true, null);
    }

    private UINavigationController FindNavigationController()
    {
        //Check to see if the rootviewcontroller is the navigationcontroller.
        foreach (var window in UIApplication.SharedApplication.Windows)
        {
            if (window.RootViewController == null)
            {
                continue;
            }
            if (window.RootViewController.NavigationController != null)
                return window.RootViewController.NavigationController;
            else
            {
                UINavigationController val = GetSubController(window.RootViewController.ChildViewControllers);
                if (val != null)
                    return val;
            }
        }
        //  var viewcontroller = UIApplication.SharedApplication.KeyWindow.RootViewController as UIViewController;
        // viewcontroller.NavigationController = new UINavigationController();
        // return new UINavigationController(viewcontroller);
        return null;
    }

    private UINavigationController GetSubController(UIViewController[] controllers)
    {
        foreach (var controller in controllers)
        {
            if (controller.NavigationController != null)
                return controller.NavigationController;
            else
            {
                UINavigationController val = GetSubController(controller.ChildViewControllers);
                if (val != null)
                    return val;
            }
        }
        return null;
    }
}

実際のイメージピッカーの表示設定を行います。キャンセルされた場合は画面を終了し、写真が選択された場合は、ストリームに変換し、バイト配列にコピーしてから再度別のストリームでApp.csに受け渡しています。(※バイト配列にコピーすることにより、GCによるデータ損失を防いでいます。)
ImagePickerController.cs
using CoreGraphics;
using System.IO;
using Xamarin.Forms;
public class ImagePickerController : UIImagePickerController
{
    UIImageView _imageView;

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        View.BackgroundColor = UIColor.White;
        var closeButtonItem = new UIBarButtonItem("Close", UIBarButtonItemStyle.Plain, this, null);
        this.NavigationItem.LeftBarButtonItem = closeButtonItem;

        _imageView = new UIImageView(new CGRect(10, 150, 300, 300));
           
        // set our source to the photo library
        this.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;

        // set what media types
        this.MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary);

        this.FinishedPickingMedia += Handle_FinishedPickingMedia;
        this.Canceled += Handle_Canceled;
    }

    // Do something when the
    void Handle_Canceled(object sender, EventArgs e)
    {
        Console.WriteLine("picker cancelled");
        this.DismissModalViewController(true);
    }

    // This is a sample method that handles the FinishedPickingMediaEvent
    protected void Handle_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
    {
        // determine what was selected, video or image
        bool isImage = false;
        switch (e.Info[UIImagePickerController.MediaType].ToString())
        {
            case "public.image":
                Console.WriteLine("Image selected");
                isImage = true;
                break;

            case "public.video":
                Console.WriteLine("Video selected");
                break;
        }

        Console.Write("Reference URL: [" + UIImagePickerController.ReferenceUrl + "]");

        // get common info (shared between images and video)
        NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
        if (referenceURL != null)
            Console.WriteLine(referenceURL.ToString());

        // if it was an image, get the other image info
        if (isImage)
        {
            // get the original image
            UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
            if (originalImage != null)
            {
                // do something with the image
                Console.WriteLine("got the original image");
                _imageView.Image = originalImage;
            }
            // get the edited image
            UIImage editedImage = e.Info[UIImagePickerController.EditedImage] as UIImage;
            if (editedImage != null)
            {
                // do something with the image
                Console.WriteLine("got the edited image");
                _imageView.Image = editedImage;
            }
            //- get the image metadata
            NSDictionary imageMetadata = e.Info[UIImagePickerController.MediaMetadata] as NSDictionary;
            if (imageMetadata != null)
            {
                // do something with the metadata
                Console.WriteLine("got image metadata");
            }
        }
        // if it's a video
        else
        {
            // get video url
            NSUrl mediaURL = e.Info[UIImagePickerController.MediaURL] as NSUrl;
            if (mediaURL != null)
            {
                //
                Console.WriteLine(mediaURL.ToString());
            }
        }

        if (_imageView.Image != null)
        {
//取得した画像をバイト配列にコピーして別ストリームで読み直す
            byte[] byteArray = null;
            using (Stream imageStream = _imageView.Image.AsPNG().AsStream())
            {
                imageStream.Position = 0;
                using (MemoryStream ms = new MemoryStream())
                {
                    imageStream.CopyTo(ms);
                    byteArray = ms.ToArray();
                }
            }
            var imageSource = ImageSource.FromStream(() => new MemoryStream(byteArray));
            App.SetPickupImageSource(imageSource);
        }

        // dismiss the picker
        this.DismissModalViewController(true);
    }
}


尚、info.plistにフォトライブラリの使用許可を求める記述が必要です。
info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>フォトライブラリを利用します。</string>


参考URL
https://developer.xamarin.com/recipes/ios/media/video_and_photos/choose_a_photo_from_the_gallery/


3.Androidの場合

単純に画像インテントを呼び出しているのみです。写真を表示するアプリが標準で組み込まれていると思いますが、無ければ別途必要です。
ImageService.cs
public class ImageService : IImageService
{
    /// <summary>
    /// イメージ選択画面を表示する(外部アプリ)
    /// </summary>
    public void ShowImageGallery()
    {
        //アクティビティを取得する
        var activity = (Activity)(Forms.Context);

        //ギャラリーを表示する
        var imageIntent = new Intent();
        imageIntent.SetType("image/*");
        imageIntent.SetAction(Intent.ActionGetContent);
        activity.StartActivityForResult(
            Intent.CreateChooser(imageIntent, "Select photo"), 0);
    }
}

選択された写真をMainActivity.OnActivityResultで受け取ります。受け取ったデータからファイルパスを取得し、App.csに渡しています。
MainActivity.cs
using Android.Database;
public class MainActivity
{
    protected override void OnActivityResult(int requestCode, Result resultCode, global::Android.Content.Intent data)
    {
        try
        {
            base.OnActivityResult(requestCode, resultCode, data);

            //ImageGalleryからの戻り値
            if (resultCode == Result.Ok)
            {
                string filePath = this.GetSelectedFilePath(data);
                App.SetPickupImageSource(filePath);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostict.Debug.WriteLine(ex.Message + System.Environment.NewLine + ex.StackTrace);
        }
    }

    //イメージギャラリーの戻り値から画像のファイルパスを取得する
    private string GetSelectedFilePath(Intent data)
    {
        string filePath = String.Empty;
ICursor cur = null;
try
{
// 選択した画像のパスを取得する
string[] columns = { Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data };
cur = this.ContentResolver.Query(data.Data, columns, null, null, null);
if (cur != null &&
cur.MoveToFirst())
{
filePath = cur.GetString(0);
}
return filePath;
}
finally
{
if (cur != null)
{
cur.Close();
}
}
    }
}

参考URL
http://mslgt.hatenablog.com/entry/2015/05/04/103617

※尚、今回はローカルからの画像取得についてのみご紹介しています。
Androidの場合で、GoogleDrive等のクラウドからの画像取得については以下の記事にてご紹介しています。
http://itblogdsi.blog.fc2.com/blog-entry-167.html


4.使用方法

TestPage.xaml
public TestPage()
{
    //写真タップ時の遷移
    TapGestureRecognizer tgr = new TapGestureRecognizer();
    tgr.Tapped += (s, e) =>
    {
        this.OnTapped(s, e);
    };
    this.imgPicture.GestureRecognizers.Add(tgr);
}

void OnTapped(object sender, EventArgs e)
{
    App.ResultPage = this;
    DependencyService.Get<IImageService>().ShowImageGallery();
}


※イメージギャラリーで選択した写真は、ImageコントロールのImageSourceに格納されています。(例:this.imgPicutre.Source)

今回は写真の戻りをApp.cs経由で取得するようにしています。もっと良い方法があるかもしれません。今後も勉強が必要です。。。





当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
http://itblogdsi.blog.fc2.com/blog-entry-81.html


関連記事

コメント

コメントの投稿
非公開コメント

アルバム

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語・データベース】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
Oracle10g SQLServer2008R2 SQLAnywhere8/11/16
ActiveReport CrystalReport ReportNet(IBM)
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。