Xamarin.Forms上でのカメラのプレビュー表示をちょっと調べたので方法のメモです。
今回は Android でプレビューを表示する方法を、次回はWindows Phone 8上でプレビューを表示する方法を解説します。
※iOSの実装については触れません。もってないので!
サンプルプログラムをGitHubで公開しています。
本記事のコードはこれを簡略化したものですので、詳しい実装はそちらをご覧下さい。
Custom Renderer
Xamarin.Forms にはハードウェアの抽象化APIがありません。
カメラプレビューを表示するには
- Android なら SurfaceView か何かに Android.Hardware.Camera のプレビューを表示させる
- WindowsPhone なら、Canvas の Background に VideoBrush を設定して、Camera のプレビューを流しこむ
といった方法が定石ですが、Xamarin.Form のPCLプロジェクトからはそもそもカメラオブジェクトに触れることすらできません。
「カメラのプレビューを表示するコントロール」が用意されていればいいのにと思うのですが、もちろんありません。
幸い、Xamarin.Forms にはプラットフォーム固有の機能を用いるための機構が備わっており、それを用いて独自の View のレンダラ(Custom Renderer)を作成することができます。
今回はこの Custom Renderer を用いてカメラプレビューの表示を実現します。
※この記事では Custom Renderer の基本的な使い方は扱いません。
Custom Renderer について詳しくは以下の Xamarin 公式文書をご覧下さい。
Customizing Controls for Each Platform
PCLプロジェクトで作るもの
PCLプロジェクトでは、カスタムレンダラを作成する元となる View を定義します。
今回はXamarin.Forms.Viewクラスを直接継承した CameraPreview クラスを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
public class CameraPreview : View { /// <summary> /// 写真を撮影する。 /// PCLから使う。 /// </summary> public void TakePicture() { if (PictureRequired != null) { PictureRequired(this, new EventArgs()); } } /// <summary> /// 写真が撮れたことを通知する。 /// カスタムレンダラから使う /// </summary> /// <param name="image">撮影された画像</param> public void OnPictureTaken(Models.IImage image) { if (image == null) { throw new ArgumentNullException("image"); } if (PictureTaken != null) { PictureTaken(this, new PictureTakenEventArgs(image)); } } /// <summary> /// TakePicture()が呼ばれた時に発生するイベント /// </summary> public event EventHandler PictureRequired; /// <summary> /// 写真が撮られた時に発生するイベント /// </summary> public event EventHandler<PictureTakenEventArgs> PictureTaken; } public sealed class PictureTakenEventArgs : EventArgs { public PictureTakenEventArgs(Models.IImage image) { Image = image; } public Models.IImage Image { get; private set; } } |
IImageインターフェイスはプラットフォーム毎に異なるビットマップの取扱いを抽象化する為のクラスで、ここでは以下のように定義しています。
(今回のサンプルではプラットフォーム固有のビットマップを利用していないので、直接ImageSourceを扱っても問題ありません。)
1 2 3 4 |
public interface IImage { ImageSource AsImageSource(); } |
まず PCLプロジェクトで準備するものは、これで全てです。
このクラスは
- PCL側から写真撮影
- プラットフォーム毎のプロジェクトから撮影した写真の受け渡し
の双方に使うため、TakePicture() と OnPictureTaken() の双方を public として定義しています。
Android 用プロジェクトでの実装
Android では ViewRenderer クラスを継承したクラスを作る事により、View の派生クラスに対するカスタムレンダラを実装できます。
Xamarin.Forms.dllの更新
ViewRenderer にはGeneric版と非Generic版があり、Generic版を使うにはプロジェクトで参照している Xamarin.Forms.dll を Version 1.2.2.* 以降にアップデートする必要があります。
NuGet パッケージマネージャから簡単にできますので、ぱっとやってしまいましょう。
カスタムレンダラの実装
次にカスタムレンダラを実装します。
継承する Platform.Android.ViewRenderer<TView, TNativeView> の TView にはPCL内で定義したクラス、TNativeView にはカスタムレンダラで実際に表示させるプラットフォーム固有の View を指定します。
今回は Xamarin.Forms の View に CameraPreview クラス、実際に表示させるのは Android.Views.SurfaceView なので、ViewRenderer<CameraPreview, SurfaceView> を継承した CameraPreviewRenderer クラスを作成します。(全コード)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public class CameraPreviewRenderer : ViewRenderer<CameraPreview, SurfaceView>, ISurfaceHolderCallback { Camera camera_ = null; protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e) { base.OnElementChanged(e); if (e.OldElement == null) { // 最初に生成された時だけここを通る。 var preview = e.NewElement; preview.PictureRequired += preview_PictureRequired; var surfaceView = new SurfaceView(Context); surfaceView.Holder.AddCallback(this); SetNativeControl(surfaceView); } } void preview_PictureRequired(object sender, EventArgs e) { CameraPreview preview = sender as CameraPreview; if (camera_ != null && preview != null) { camera_.TakePicture(null, null, new DelegatePictureCallback { PictureTaken = (data, camera) => { // jpegデータをMemoryStreamに書き込む MemoryStream ms = null; try { ms = new MemoryStream(data.Length); ms.Write(data, 0, data.Length); ms.Flush(); ms.Seek(0, SeekOrigin.Begin); // MemoryStreamからImageSourceを生成 preview.OnPictureTaken(new Models.AndroidImage { ImageSource = ImageSource.FromStream(() => ms) }); } catch { if (ms != null) { ms.Dispose(); } throw; } } }); } } // ------- 中略 ------- } |
サンプルではSurfaceViewを作成し、そこにカメラのプレビューを表示します。
まずOnElementChanged() 関数をオーバーライドし、e.OldElement == null のとき、つまり最初にCameraPreviewクラスのオブジェクトがこのレンダラに設定される時に、ビューの生成処理を行います。
プレビューを表示する SurfaceView を作成し SetNativeControl() メソッドで、生成した SurfaceView をカスタムレンダラのルート要素に設定します。
なお、ここではカメラを初期化せず SurfaceView の SurfaceCreated/SurfaceDestroyed イベントでカメラの初期化・破棄を行います。
※ここでは省いていますので、ソースコードを直接確認してください。
このとき OnElementChanged() の e.NewElement には CameraPreview クラスのインスタンスが入っていますので、これを取得して写真要求イベントのハンドラも設定します。
写真の要求があったら Camera.TakePicture() メソッドを呼び、そのコールバックで受け取ったjpegデータを一旦 MemoryStream に保存し、そこから ImageSource を作成し、撮影完了通知を行います。
カスタムレンダラのExport
カスタムレンダラを実装したら、次Xamarin.Forms.ExportRendererAttribute 属性を用いて作成したカスタムレンダラを Export します。
方法は簡単で、以下の一行をコードの戦闘部分などに記入するだけです。
アセンブリにこの属性を持たせることで、この(Androidの)プロジェクトにおいて、CameraPreview クラスのレンダラには CameraPreviewRenderer クラスが利用されるようになります。
1 |
[assembly: ExportRenderer(typeof(CameraPreview), typeof(XamarinFormsCameraPreview.Droid.Renderers.CameraPreviewRenderer))] |
これで、カスタムレンダラの実装は完了です。
カスタムレンダラを利用する – PCLでの実装再び
カスタムレンダラが完成したので、次はそれを表示させましょう。
ここからはまたPCLプロジェクトをいじっていきます。
まずはカメラのプレビューを表示させるページを以下のように作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamarinFormsCameraPreview.Views.CapturePage"> <AbsoluteLayout BackgroundColor="Gray"> <Grid x:Name="gridCameraPreview_" AbsoluteLayout.LayoutBounds="0, 0, 1.0, 1.0" AbsoluteLayout.LayoutFlags="All"/> <Button Text="TakePicture" Clicked="OnCaptureButtonClicked" HorizontalOptions="Center" VerticalOptions="Center" AbsoluteLayout.LayoutBounds="0, 1.0, 1.0, 0.1" AbsoluteLayout.LayoutFlags="All" /> </AbsoluteLayout> </ContentPage> |
単純にGridを全体に表示させ、画面下部分に撮影ボタンを設置しています。
実はこのGridが肝で、カスタムレンダラを利用するビューをXAML上で直接配置すると、Xamarin.Forms はエラーを吐いてしまいます。
これを防ぐため、CameraPreview クラスのインスタンス作成はコードビハインドで行います。
CapturePageのコードビハインドは以下のようになっています。 (全コード)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public partial class CapturePage { CameraPreview preview_ = null; public CapturePage() { InitializeComponent(); preview_ = new CameraPreview(); preview_.PictureTaken += preview__PictureTaken; gridCameraPreview_.Children.Add(preview_); } void preview__PictureTaken(object sender, PictureTakenEventArgs e) { // ------ 中略 ------ // e.AsImageSource()で取得した写真画像をViewImagePageで表示する。 } void OnCaptureButtonClicked(object sender, EventArgs e) { preview_.TakePicture(); } } |
InitializeComponent() の後に CameraPreview クラスのインスタンスを作成し、GridのChildrenに追加しています。
これでプラットフォームに応じて作成された Custom Renderer を利用するビューが生成され、カメラのプレビューが表示されます。
また、撮影(Take Picture)ボタンが押された時には OnCaptureButtonClicked() メソッドが呼ばれ、CameraPreview に対して写真撮影の要求をします。
動作確認
実際にアプリケーションを動かして動作を確認してみましょう。
Android上でアプリケーションを起動すると、以下のようにカメラのプレビューが表示されます。
プレビュー画面のアスペクト比や解像度、フォーカス、回転角度など本来はCameraPreviewRendererの中で設定しなければならないのですが、今回は主眼でないため省いています。
ここでTakePictureボタンをタップするとその場で写真が撮影され、以下の撮影写真のプレビュー画面へ遷移します。
正常に写真が撮影され、画像として取得できていることが確認できました。
Action Bar の Home ボタンか、端末の Back ボタンをタップすると、またカメラのプレビュー画面へ戻ります。
今回は、Xamarin.Formsでカメラのプレビューを表示する方法、特にAndroidでの実装について解説しました。
次回は Windows Phone 8 での実装を紹介したいと思います。
Windows Phone 8 での実装も Android のものとほとんど同じで、ここまでできれば簡単に実装ができます。
ピンバック: Xamarin.Formsでカメラを使う – Windows Phone 8編 | スタジオ大破の自転車大破日記
。あなたのページが美しいです、あなたのグラフィックが優れている、といただきました!より、あなたが何について話しているのかに関連しているソースを使用しています。 Youreのは確かに1万人で、良い仕事を維持します!
ニューバランス M1400 DK ブラウン/タン/オフホワイト 『MADE IN USAモデル』 NEWBALANCE M1400 DK BROWN/TAN/OFF WHITE メンズ スニーカー 到着後レビューのお約束で『送料無料』:Foot Time <a href="http://pamelacao.com.ar/gohotnb/137.html" rel="nofollow ugc">http://pamelacao.com.ar/gohotnb/137.html</a>
That’s a clever answer to a tricky quteoisn
あなたがかもしれないここウェブログ を持っています!あなたはいくつかは、私のブログの記事を招待するにしたいでしょうか?かなり
[url=http://balticshuttle.com/dearsbaby/18.html]【送料無料】【選べる2色!2個セット】ペリカン ミニ 15Lスタックストー stacksto, pelican mini収納 収納ボックス 収納ケース おもちゃ箱※北海道?沖縄?離島は送料無料対象外【インテリア?[/url]
アイブ氏は、スタイルと一緒に、私の個人的なブログは、コード化され得るために、途中でいくつかのアイデアを得ることを望んネット上であなたの現在のスタイルを模索して素晴らしいです。あなたは自分でそれをコーディングしましたか、あなたの代わりにそれを行うためのコーダーを募集しましたか?
ホットテックス 布団 敷きカバー セミダブル 肌布団 掛布団 タオルケット 寝具 布団 ふとん 快適 快眠 新着 送料込み 【送料無料】:家具通販のロウヤ <a href="http://www.pierpaoli.com/elepshe/505.html" rel="nofollow ugc">http://www.pierpaoli.com/elepshe/505.html</a>
Hello!
Hello!
If time is money you’ve made me a weeahtilr woman.