この前の記事で、カメラのプレビューをViewに表示した。次は、その画像を保存してみよう。
カメラを撮るには、単にCamera#takePicture()メソッドを呼ぶだけでよい。このメソッドは3つのコールバックを指定することになっているが、ここではjpgの画像を得るため、最後のPictureCallbackのみ渡している。
以下にプログラムの全体を示す。
public class MyCameraView extends SurfaceView{ final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HH-mm-ss-SSS"); // ファイル保存のルート /sdカードのルート/アプリ名称/ 以下に画像が保存される final String ROOT_PATH = Environment.getExternalStorageDirectory() + File.separator + getResources().getString(R.string.app_name) + File.separator; // カメラオブジェクト private Camera camera = null; /** * サーフェイスコールバック */ private final SurfaceHolder.Callback surfaceCallback = new Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { // SDカードの状態を調べる String state = Environment.getExternalStorageState(); if( Environment.MEDIA_MOUNTED.equals(state)){ // SDカードが使える状態ならばカメラをopenする // カメラを取得 camera = Camera.open(); try { camera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } // ディレクトリが無い場合は作っておく final File f = new File(ROOT_PATH); if( !f.exists() ){ f.mkdir(); } Toast.makeText( getContext(), "SD card ok. camera start.", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText( getContext(), "SD card can not write.", Toast.LENGTH_SHORT).show(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // プレビュー開始 if(camera != null) camera.startPreview(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // プレビュー停止 if(camera!=null){ camera.stopPreview(); camera.release(); camera = null; } } }; /** * 撮影完了時に呼ばれるコールバック */ private final Camera.PictureCallback jpgPictureCallback = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Log.d(null, "onPictureTaken"); // 画像を保存 final String path= ROOT_PATH + dateFormat.format(new Date()) + ".jpg"; FileOutputStream fos = null; try { fos = new FileOutputStream(path); } catch (FileNotFoundException e1) { Log.d("MyCameraView", e1.getMessage() ); } if( fos != null){ try { fos.write(data); } catch (IOException e1) { e1.printStackTrace(); } try { fos.close(); fos = null; } catch (IOException e) { e.printStackTrace(); } } //プレビュー再開 camera.startPreview(); } }; /** * コンストラクタ * @param context */ public MyCameraView(final Context context) { super(context); final SurfaceHolder holder = getHolder(); // これをしないと落ちる・・・謎である?? 3.1で廃止されたようだ holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // コールバック登録 holder.addCallback(surfaceCallback); } /* (非 Javadoc) * @see android.view.View#onTouchEvent(android.view.MotionEvent) */ @Override public boolean onTouchEvent(MotionEvent event) { // タッチ時に写真を撮る if (event.getAction()==MotionEvent.ACTION_DOWN) { if(camera != null) camera.takePicture(null,null,jpgPictureCallback); } return true; } }
22行目、SDカードの状態を調べ、SDカードに書き込める状態の時のみ、カメラを使えるようにした。
71行目に記述しているのがPictureCallbackである。このコールバックをtakePicture()メソッド3番目の引数に渡すと、撮影が成功したらjpegの画像がdataに入ってくる。
それを、普通にFileOutputStreamを使って書き出している。なお、このコールバックが呼ばれると、カメラのプレビューが止まった状態になっているため、104行目でstartPreview()メソッドを呼び、プレビューを再開している。
128行目、画面がタッチされたタイミングでCameraのtakePicture()メソッドを呼び出している。
ここまでのプログラムで、上手く動作すれば、画面をタッチすると
/sdcard/アプリ名称/下にカメラ画像が保存されるだろう。