Xamarin.Forms 1.3.xを使用してカメラにアクセスする方法について、自己完結型の短い例を挙げることはできますか?ネイティブカメラアプリケーションを呼び出して、結果の画像を取得するだけでいいでしょう。 Xamarin.Formsページでライブビューを表示するのは素晴らしいことです。
すでにXamarin.MobileとXamarin.Forms.Labsを使用しようとしましたが、両方のプラットフォームで動作するソリューションを得ることができませんでした(AndroidおよびiOSは今のところ)。ほとんどのコードスニペット(stackoverflowを含む)ウェブ上で見つかったものは不完全です。たとえば、IMediaPickerオブジェクトの実装を表示していないか、写真を撮るためのメソッドを固定している場所です。
私はついに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);
}
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実装では、カスタムレンダラーを作成します。したがって、新しいファイル「CustomContentPageRenderer」を追加し、usingステートメントの直後に対応するAssembly属性を追加します。
[Assembly:ExportRenderer(typeof(ContentPage), typeof(CustomContentPageRenderer))]
CustomContentPageRenderer
はPageRenderer
から継承します:
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);
James Montemagnoの MediaPlugin を試してください。
Package Manager Console を使用してプラグインをインストールするには、単にInstall-Package Xam.Plugin.Media -Version 2.6.2
と入力して実行するか、 NuGetパッケージの管理... に移動し、 Xam.Plugin.Media
と入力して、プラグインをインストールします。 (プラグインは、クライアントプロジェクトを含むすべてのプロジェクトにインストールする必要があります)
readme.txtが求められ、指示に従います。その後、 次のコード を(必要に応じて)共有プロジェクトに追加します。上記のreadme.txtファイルで従うべき指示は次のとおりです。
BaseActivityまたはMainActivity(Xamarin.Formsの場合)に次のコードを追加します。
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
また、新しい厳密モードに準拠するために、いくつかの構成ファイルを追加する必要があります。
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はアプリのパッケージ名に設定する必要があります!
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はアプリのパッケージ名に設定する必要があります!
デバイスのカメラと写真/ビデオライブラリにアクセスするには、アプリがNSCameraUsageDescription
とNSPhotoLibraryUsageDescription
の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");
アプリで非同期カメラキャプチャを実行するために必要なものは次のとおりです。
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;
}
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;
}
}
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();
}
[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;
}
}