Androidでカメラをつかった簡単なプログラムを作成してみた。(ソースはこちら)
標準でついているカメラにはセルフタイマーがないので、セルフタイマー専用のカメラを作ってみる。

残り秒数がカウントダウンされる
★カメラの使用方法については、開発者サイトの以下のリンクを参照(android.hardware.Camera)
要旨だけまとめると
①カメラのインスタンスを取得 open(int).
②既存の設定(デフォルト)を取得 getParameters().
③必要であれば、2で取得した Camera.Parameters オブジェクトを変更しsetParameters(Camera.Parameters) を呼び出して変更した設定を反映
④必要なら setDisplayOrientation(int). で画像の向きを設定
⑤初期化済みの SurfaceHolder を setPreviewDisplay(SurfaceHolder). に渡す。これがないとプレビューができない。
⑥startPreview() を呼び出してプレビューを開始。写真を撮る前にプレビューが開始されないといけない。
⑦takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback) を呼び出すと撮影される。イメージが作成されると各コールバックが呼び出されるので、そこでデータを取得できる。
⑧撮影後はプレビューは止まっているので、再開するには startPreview() を呼び出す。
⑨stopPreview() でプレビューを停止
⑩他のアプリケーションがカメラを使えるように、カメラを release() する。アプリケーションは onPause() でカメラをリリースしなければならない(そして onResume() で再び open() する).
上のリンクの情報はよくまとめられているので、ここ情報だけでほぼ十分だった。(個人的な意見ですが、Appleの開発者サイトに比べるとGoogleの開発者サイトのほうがわかりやすいですね。)
■このプログラムの仕様
・プレビュー画面左上のボタンを押すと、10秒後に撮影される。
・プレビュー画面上で残り秒数がカウントダウンされる。
・撮影した画像のサムネイルが左下に表示され、サムネイルをクリックすると画像が表示される。
・画像は最後の一枚しか保存されない。(内蔵メモリに保存するので・・・)
プレビュー用画面のレイアウトは、プレビュー表示用のSurfaceViewの上に、オーバーレイ状にボタンとラベルを配置。
オーバーレイ状に画面を重ねるので、一番下のレイアウトはFrameLayout、その上にSurfaceViewとRelativeLayoutを重ねる。
RelativeLayoutの左上にタイマー開始のボタン(カメラのアイコン)、真ん中にカウントダウン表示用のラベル、左下にサムネイル表示用のイメージボタンを配置。
なお、プレビュー画面の方向を変えることができなかったので、画面の向きはLandscapeに固定する。
画像確認用の画面はImageViewのみで構成する。
また、Manufestに以下の項目が必要
<uses-permission android:name=”android.permission.CAMERA” />
<uses-feature android:name=”android.hardware.camera” />
■各処理の説明
1.初期化処理
(1)OnCreate
- まずプレビュー画面が表示されるように設定(setContentView)
- 画面の向きをLandscapeに設定
- 各コントロールの参照を取得
- SurfaceViewのSurfaceHolderにコールバックのリスナーを設定(Activityにコールバックを実装するので、this を指定)
- SurfaceViewのSurfaceHolderのTypeを SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS に設定。※これを設定しないとプレビュー開始時にエラーになる。
- 画面上の配置するコントロールのVisibiityを設定。ボタンはVisible、カウントダウン用のラベルはInvisible
- ボタンが押された際の動作を記述
(2)OnResume
- カメラのインスタンスを取得
open(int).(①)
(3)surfaceCreated ※SurfaceHolderのコールバック
Surfaceが初期化されたので、カメラのsetPreviewDisplayに設定する(⑤)
(4)surfaceChanged ※SurfaceHolderのコールバック
- Surfaceのサイズなどが決まったタイミングで呼び出されると思われる。ここでgetParametersでパラメータを取得する(②)。後で画面のサイズを取得する際に使用する。
- 他にも設定したいParameterがあればここで設定(③、④)したらよいのではないかと思われるが、今回は特に必要がなかったので設定していない。(※設定するタイミングについては、実はまだよくわかっていない。現状では、SuspendからResumeしたときに、例外が発生して落ちてしまうことが多い。カメラのインスタンスを取得できないのが原因らしいが、このへんのタイミングについては、もっと調べる必要がある。)
- カメラのstartPreviewを呼び出す。
2.タイマーのスタート
(1)ボタンが押されたら、カウントダウンのラベルをVisibleに設定し、CountDownTimerを作成してスタートさせる。
(2)CountDownTimerのonTickでカウントダウンのラベルの数字を更新。
(3)CountDownTimerのonFinishでカウントダウンのラベルをInvisibleに戻し、カメラのtakePictureを呼び出す。
3.撮影
(1)カメラのtakePictureを呼び出す。このプログラムではJPEGデータのコールバックだけを指定。
(2)JPEGデータのコールバック(onPictureTaken)で、データを取得してファイルに保存。サムネイル用のビットマップを更新した後、カメラのstartPreviewを呼び出す。
4.画像の保存
★内蔵メモリへのデータ保存については以下のリンクを参照
(1)各アプリケーション用に指定された領域にファイルを作成する。(openFileOutput)
(2)画像データは byte[] なので、これを FileOutputStream に書き込んで、閉じる
FileOutputStream fos = this.openFileOutput(name, Context.MODE_WORLD_WRITEABLE); fos.write(data); fos.close();
5.サムネイル表示の更新
(1)getFileStreamPath で、4で各アプリケーション用に指定された領域に保存したファイルのパスを取得。
(2)カメラのParameterから画像サイズを取得し、サムネイル用にするには元の大きさの何%にしたらよいかを計算。
(3)BitmapFactory.decodeFile を呼び出し、Bitmap を作成。
(4)サムネイル表示用のImageButtonにBitmapを設定。
■わかったこと、及び課題
1.保存したファイルにアクセスできない
今回は、openFileOutput を使って、内蔵メモリの各アプリケーション用に指定された領域にファイルを作成した。ここは他のアプリケーションからはアクセスできないように設計されているのかもしれない。画像のようなデータは外部メモリに保存したほうがよかった。撮った画像データにアクセスできないと意味が無いので、後で修正します。
2.レベル8以上でないと使えない便利なAPIが多い
サムネイルを作成したり、外部メモリへの保存先を指定したり、といった「これは使いたい」と思ったAPIが、レベル8以上の指定になっていて使うことができず、残念な思いをした。
3.SuspendからResumeしたときに、例外が発生して落ちてしまうことが多い。
内蔵のカメラではそのようなことはないので、なにかが足りないはずだ。エラーの内容を見るとカメラのインスタンスを取得できていないようだ。カメラの準備ができてないタイミングでアクセスしようとしてしまっているのか?まだ調査が必要。
4.オートフォーカス
Manufestに以下のように設定しておけば自動的にオートフォーカスになるのかと思っていたが
<uses-feature android:name=”android.hardware.camera.autofocus” android:required=”false” />
どうも違うらしい。オートフォーカス用のコールバックを実装しないといけないようなので、今回はパス。


2011年8月7日 3:13 午後 |
[…] 前回作成したセルフタイマー専用カメラに機能追加。 […]
2011年8月27日 9:01 午前 |
[…] これで、タイマーまたはSMSを受信したタイミングで、カメラで画像を撮影し、Picasa へのアップロードを行うアプリができた。(→ダウンロード) 共有:共有メールFacebookTwitterLike this:Like一番乗りで「Like」しませんか。 […]