web-dev-qa-db-ja.com

Xamarin.Formsを使用したカメラアクセス

Xamarin.Forms 1.3.xを使用してカメラにアクセスする方法について、自己完結型の短い例を挙げることはできますか?ネイティブカメラアプリケーションを呼び出して、結果の画像を取得するだけでいいでしょう。 Xamarin.Formsページでライブビューを表示するのは素晴らしいことです。

すでにXamarin.MobileとXamarin.Forms.Labsを使用しようとしましたが、両方のプラットフォームで動作するソリューションを得ることができませんでした(AndroidおよびiOSは今のところ)。ほとんどのコードスニペット(stackoverflowを含む)ウェブ上で見つかったものは不完全です。たとえば、IMediaPickerオブジェクトの実装を表示していないか、写真を撮るためのメソッドを固定している場所です。

30
Falko

私はついにiOSとAndroidの最小限のソリューションを作成しました。

共有プロジェクト

まず、共有コードを見てみましょう。共有Appクラスとプラットフォーム固有のコードを簡単にやり取りするために、public static App内に静的なInstanceを保存します。

public static App Instance;

さらに、後でコンテンツで満たされるImageを表示します。そこで、メンバーを作成します。

readonly Image image = new Image();

Appコンストラクター内で、Instanceを格納し、ページコンテンツを作成します。これは、単純なbuttonと前述のimageです。

public App()
{
   Instance = this;

   var button = new Button {
       Text = "Snap!",
       Command = new Command(o => ShouldTakePicture()),
   };

   MainPage = new ContentPage {
       Content = new StackLayout {
       VerticalOptions = LayoutOptions.Center,
           Children = {
                    button,
                    image,
           },
       },
   };
}

ボタンのクリックハンドラーは、イベントShouldTakePictureを呼び出します。これはパブリックメンバーであり、プラットフォーム固有のコード部分が後で割り当てられます。

public event Action ShouldTakePicture = () => {};

最後に、キャプチャした画像を表示するためのパブリックメソッドを提供します。

public void ShowImage(string filepath)
{
    image.Source = ImageSource.FromFile(filepath);
}

Androidプロジェクト

On Android MainActivityを変更します。最初に、キャプチャした画像ファイルのパスを定義します。

static readonly File file = new File(Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures), "tmp.jpg");

OnCreateの最後に、作成されたInstanceの静的Appを使用し、匿名イベントハンドラーを割り当てます。これにより、キャプチャーのために新しいIntentが開始されます画像:

App.Instance.ShouldTakePicture += () => {
   var intent = new Intent(MediaStore.ActionImageCapture);
   intent.PutExtra(MediaStore.ExtraOutput, Uri.FromFile(file));
   StartActivityForResult(intent, 0);
};

最後になりましたが、私たちの活動は結果の画像に反応する必要があります。共有ShowImageメソッドにファイルパスをプッシュするだけです。

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
   base.OnActivityResult(requestCode, resultCode, data);
   App.Instance.ShowImage(file.Path);
}

それはそれについてです! 「AndroidManifest.xml」内で「Camera」および「WriteExternalStorage」権限を設定することを忘れないでください!

IOSプロジェクト

IOS実装では、カスタムレンダラーを作成します。したがって、新しいファイル「CustomContentPageRenderer」を追加し、usingステートメントの直後に対応するAssembly属性を追加します。

[Assembly:ExportRenderer(typeof(ContentPage), typeof(CustomContentPageRenderer))]

CustomContentPageRendererPageRendererから継承します:

public class CustomContentPageRenderer: PageRenderer
{
    ...
}

ViewDidAppearメソッドをオーバーライドし、次の部分を追加します。

カメラを参照する新しいイメージピッカーコントローラーを作成します。

var imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };

ShouldTakePictureイベントが発生するとすぐに、画像ピッカーコントローラーを提示します。

App.Instance.ShouldTakePicture += () => PresentViewController(imagePicker, true, null);

写真を撮った後、MyDocumentsフォルダーに保存し、共有ShowImageメソッドを呼び出します。

imagePicker.FinishedPickingMedia += (sender, e) => {
            var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
            InvokeOnMainThread(() => {
                image.AsPNG().Save(filepath, false);
                App.Instance.ShowImage(filepath);
            });
            DismissViewController(true, null);
        };

最後に、画像取得プロセスのキャンセルを処理する必要があります。

imagePicker.Canceled += (sender, e) => DismissViewController(true, null);
34
Falko

James Montemagnoの MediaPlugin を試してください。

Package Manager Console を使用してプラグインをインストールするには、単にInstall-Package Xam.Plugin.Media -Version 2.6.2と入力して実行するか、 NuGetパッケージの管理... に移動し、 Xam.Plugin.Mediaと入力して、プラグインをインストールします。 (プラグインは、クライアントプロジェクトを含むすべてのプロジェクトにインストールする必要があります)

readme.txtが求められ、指示に従います。その後、 次のコード を(必要に応じて)共有プロジェクトに追加します。上記のreadme.txtファイルで従うべき指示は次のとおりです。

Android Project

BaseActivityまたはMainActivity(Xamarin.Formsの場合)に次のコードを追加します。

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
    PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}  

また、新しい厳密モードに準拠するために、いくつかの構成ファイルを追加する必要があります。

  1. AndroidManifest.xmlの<application>タグ内に以下を追加します。

    <provider Android:name="Android.support.v4.content.FileProvider" 
              Android:authorities="YOUR_APP_PACKAGE_NAME.fileprovider" 
              Android:exported="false" 
              Android:grantUriPermissions="true">
        <meta-data Android:name="Android.support.FILE_PROVIDER_PATHS" 
                   Android:resource="@xml/file_paths"></meta-data>
    </provider>
    

    YOUR_APP_PACKAGE_NAMEはアプリのパッケージ名に設定する必要があります!

  2. Xmlという新しいフォルダーをResourcesフォルダーに追加し、file_paths.xmlという新しいXMLファイルを追加します

    次のコードを追加します。

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
        <external-path name="my_images" path="Android/data/YOUR_APP_PACKAGE_NAME/files/Pictures" />
        <external-path name="my_movies" path="Android/data/YOUR_APP_PACKAGE_NAME/files/Movies" />
    </paths>
    

    YOUR_APP_PACKAGE_NAMEはアプリのパッケージ名に設定する必要があります!

IOSプロジェクトの場合

デバイスのカメラと写真/ビデオライブラリにアクセスするには、アプリがNSCameraUsageDescriptionNSPhotoLibraryUsageDescriptionのInfo.plistにキーを持っている必要があります。ライブラリのビデオ機能を使用している場合は、NSMicrophoneUsageDescriptionも追加する必要があります。これらの各キーに指定した文字列は、これらのデバイス機能へのアクセス許可を提供するように求められたときにユーザーに表示されます。

といった:

<key>NSCameraUsageDescription</key>
<string>This app needs access to the camera to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone.</string>

共有プロジェクトの場合

カメラを開いて写真を保存し、ファイルパスを含むアラートを表示するには、共有プロジェクトに次を入力します。

if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
    await DisplayAlert("No Camera", ":( No camera avaialble.", "OK");
    return;
}

var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
    PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium,
    Directory = "Sample",
    Name = "test.jpg"
});

if (file == null)
    return;

await DisplayAlert("File Location", file.Path, "OK"); 
5
Curiousity

アプリで非同期カメラキャプチャを実行するために必要なものは次のとおりです。

IOSの場合:

public async Task<string> TakePicture()
{
    if (await AuthorizeCameraUse())
    {
        var imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };

        TaskCompletionSource<string> FinishedCamera = new TaskCompletionSource<string>();

        // When user has taken picture
        imagePicker.FinishedPickingMedia += (sender, e) => {
            // Save the file
            var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
            var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
            image.AsPNG().Save(filepath, false);

            // Close the window
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);

            // Stop awaiting
            FinishedCamera.SetResult(filepath);
        };

        // When user clicks cancel 
        imagePicker.Canceled += (sender, e) =>
        {
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
            FinishedCamera.TrySetCanceled();
        };

        // Show the camera-capture window
        UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(imagePicker, true, null);

        // Now await for the task to complete or be cancelled
        try
        {
            // Return the path we've saved the image in
            return await FinishedCamera.Task;
        }
        catch (TaskCanceledException)
        {
            // handle if the user clicks cancel
        }
    }

    return null;
}

承認ルーチンが必要な場合は、info.plistのカメラの使用も記入してください。承認を取得する関数は次のとおりです。

public static async Task<bool> AuthorizeCameraUse()
{
    var authorizationStatus = AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video);

    if (authorizationStatus != AVAuthorizationStatus.Authorized)
    {
        return await AVCaptureDevice.RequestAccessForMediaTypeAsync(AVMediaType.Video);
    }
    else
        return true;
}

Androidの場合:

private TaskCompletionSource<bool> _tcs_NativeCamera;

public async Task<string> TakePicture()
{
    _tcs_NativeCamera = new TaskCompletionSource<bool>();

    // Launch the camera activity
    var intent = new Intent(MediaStore.ActionImageCapture);
    intent.PutExtra(MediaStore.ExtraOutput, Android.Net.Uri.FromFile(cameraCaptureFilePath));

    NextCaptureType = stype;

    StartActivityForResult(intent, SCAN_NATIVE_CAMERA_CAPTURE_ASYNC);

    // Wait here for the activity return (through OnActivityResult)
    var Result = await _tcs_NativeCamera.Task;

    // Return the camera capture file path
    return Result != Result.Canceled ? cameraCaptureFilePath : null;
}

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    switch (requestCode)
    {
        case SCAN_NATIVE_CAMERA_CAPTURE_ASYNC:
            _tcs_NativeCamera.SetResult(resultCode);
            break;
    }
}
1
noelicus

Xamarin iOSを介したXamarin Formsでの方法を次に示します。

これは開始するのに適したベースですが、最初にレンダリングするページが必要です。このページでは、UIApplicationを指定するだけでカメラ/フォトピッカーコントローラーのUIViewを提供できます。

https://stackoverflow.com/a/28299259/1941942

ポータブルプロジェクト

public interface ICameraProvider
{
    Task<CameraResult> TakePhotoAsync();
    Task<CameraResult> PickPhotoAsync();
}

private Command AttachImage 
{
    var camera = await DependencyService.Get<ICameraProvider>().TakePhotoAsync();
}

iOSプロジェクト

[Assembly: Xamarin.Forms.Dependency(typeof(CameraProvider))]

public class CameraProvider : ICameraProvider
{
    private UIImagePickerController _imagePicker;
    private CameraResult _result;
    private static TaskCompletionSource<CameraResult> _tcs;

    public async Task<CameraResult> TakePhotoAsync()
    {
        _tcs = new TaskCompletionSource<CameraResult>();

        _imagePicker = new UIImagePickerController { SourceType = UIImagePickerControllerSourceType.Camera };

        _imagePicker.FinishedPickingMedia += (sender, e) =>
        {
            _result = new CameraResult();
            var filepath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "tmp.png");
            var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));

            _result.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.AsPNG().ToArray())); 
            _result.ImageBytes = image.AsPNG().ToArray();
            _result.FilePath = filepath;

            _tcs.TrySetResult(_result);
            _imagePicker.DismissViewController(true, null);
        };

        _imagePicker.Canceled += (sender, e) =>
        {
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
        };

        await UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewControllerAsync(_imagePicker, true);

        return await _tcs.Task; 
    }

    public async Task<CameraResult> PickPhotoAsync()
    {
        _tcs = new TaskCompletionSource<CameraResult>();

        _imagePicker = new UIImagePickerController
        {
            SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
            MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
        };

        _imagePicker.FinishedPickingMedia += (sender, e) =>
        {

            if (e.Info[UIImagePickerController.MediaType].ToString() == "public.image")
            {
                var filepath = (e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl);
                var image = (UIImage)e.Info.ObjectForKey(new NSString("UIImagePickerControllerOriginalImage"));
                //var image = e.Info[UIImagePickerController.OriginalImage] as UIImage;

                _result.ImageSource = ImageSource.FromStream(() => new MemoryStream(image.AsPNG().ToArray()));
                _result.ImageBytes = image.AsPNG().ToArray();
                _result.FilePath = filepath?.Path;
            }

            _tcs.TrySetResult(_result);
            _imagePicker.DismissViewController(true, null);
        };

        _imagePicker.Canceled += (sender, e) =>
        {
            UIApplication.SharedApplication.KeyWindow.RootViewController.DismissViewController(true, null);
        };

        await UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewControllerAsync(_imagePicker, true);

        return await _tcs.Task;
    }
}
1
pampi