【Android】カメラで撮った画像を保存する

投稿者: | 2011年7月20日

この前の記事で、カメラのプレビューを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/アプリ名称/下にカメラ画像が保存されるだろう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です