カメラによる画像撮影 (カメラによる画像の取得) - Android

こちらの記事で紹介したカメラの映像をプレビューするプログラムに画像撮影機能を追加します。

アプリケーション作成情報

  • ProjectName : Simplecamera
  • ApplicationName : Simplecamera
  • PackageName : com.ipentec.simplecamera
  • Activity : MainActivity
  • Androidのバージョンは2.3.3にしました

UI

変更しません。デフォルトのままです。

AndroidManifest

Uses Permissionで"android.permission.CAMERA"を追加します。手順はこちらの記事を参照してください。また、SDカードに撮影した画像を保存するためUses Permissionで"android.permission.WRITE_EXTERNAL_STORAGE"も追加します。

コード

下記のコードを実装します。

MainActivity.java

package com.ipentec.simplecamera;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.ShutterCallback;
import android.view.*;
import android.view.View.*;
import android.widget.LinearLayout;
import android.app.*;
import android.widget.*;
import android.view.ViewGroup.LayoutParams;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.hardware.Camera.PictureCallback;
import android.graphics.*;
import android.provider.MediaStore;
import android.provider.MediaStore.*;
import android.content.*;
import java.io.File;
import java.io.FileOutputStream;
import android.os.Environment;

public class MainActivity extends Activity {
  private Preview mPreview;
  Camera mCamera;
  int numberOfCameras;
  int cameraCurrentlyLocked;
  int defaultCameraId;
  OverlayView overlay;
 
  
  private PictureCallback jpegListener = new PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
    if (data != null) {
      if(!sdcardWriteReady()){
        Toast.makeText(mPreview.pcontext, "SDCARDが認識されません。", Toast.LENGTH_SHORT).show();
        camera.startPreview();
        return;
      }
      FileOutputStream foStream = null;
      File file = new File(Environment.getExternalStorageDirectory().getPath() + "/cmr/");
      if(!file.exists()){
        file.mkdir();
      }
      String imgName = Environment.getExternalStorageDirectory().getPath()
        + "/cmr/" + System.currentTimeMillis() +".jpg";

      try {
        foStream = new FileOutputStream(imgName);
        foStream.write(data);
        foStream.close();

        ContentValues values = new ContentValues();
        ContentResolver contentResolver = mPreview.pcontext.getContentResolver();
        values.put(Images.Media.MIME_TYPE, "image/jpeg");
        values.put("_data", imgName);
        try {
          contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        }catch(Exception e){
          Toast.makeText(mPreview.pcontext, "再起動後に画像が認識されます。", Toast.LENGTH_SHORT).show();
          e.printStackTrace();
        }
      } catch (Exception e) {
        Toast.makeText(mPreview.pcontext, "ファイルの保存中にエラーが発生しました。", Toast.LENGTH_SHORT).show();
        e.printStackTrace();
      }
      camera.startPreview();
    }else{
      Toast.makeText(mPreview.pcontext, "データが取得できませんでした。", Toast.LENGTH_SHORT).show();
      camera.startPreview();
    }
  }
  };

  private boolean sdcardWriteReady(){
    String state = Environment.getExternalStorageState();
    return (Environment.MEDIA_MOUNTED.equals(state));
  }

	
  private ShutterCallback mShutterListener = new ShutterCallback() {
    @Override
    public void onShutter(){
      // 処理
     }
  };

  private PictureCallback rawListener = new PictureCallback() {
  @Override
    public void onPictureTaken(byte[] data, Camera camera) {
      // 保存する処理
    }
  };
	
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);        
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    mPreview = new Preview(this);        
    
    setContentView(mPreview);
	
    numberOfCameras = Camera.getNumberOfCameras();
    CameraInfo cameraInfo = new CameraInfo();
    for (int i = 0; i < numberOfCameras; i++) {
      Camera.getCameraInfo(i, cameraInfo);
      if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
        defaultCameraId = i;
      }
    }
    
    overlay = new OverlayView(this);
    LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    addContentView(overlay, lp);

    Button button_shutter = new Button(this);
    button_shutter.setText("撮影");
    LayoutParams lpb = new LayoutParams(180, 90);
    addContentView(button_shutter, lpb);
    button_shutter.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
      	ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, ToneGenerator.MAX_VOLUME);
      	toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP);

      	mCamera.takePicture(mShutterListener, rawListener, jpegListener);
      }
    });
  }
  
  @Override
  protected void onResume() {
    super.onResume();
    mCamera = Camera.open();
    cameraCurrentlyLocked = defaultCameraId;
    mPreview.setCamera(mCamera);
  }
   
  @Override
  protected void onPause() {
    super.onPause();
    if (mCamera != null) {
      mPreview.setCamera(null); 
      mCamera.release();
      mCamera = null;
    }   
  } 
}
解説
onCreateメソッド内で、OverlayViewを作成しカメラのプレビューの上にかぶせています。OverlayView内にボタンを一つ配置し、撮影ボタンを設けています。撮影ボタンのクリックイベントはButtonのインスタンス作成時に定義しています。CameraオブジェクトのTakePicture()メソッドを呼び出すとカメラ画像の撮影ができます。TakePictureメソッドの引数に撮影完了時に呼び出されるコールバックオブジェクトを与えます。今回はJpeg画像を保存するため、第三引数のjpegListenerのオブジェクトを実装します。
撮影完了時にはコールバックオブジェクトのonPictureTaken()メソッドが呼び出されます。メソッド内でSDカードに画像を保存するコードを実装します。撮影した画像データはonPictureTaken()メソッドの第一引数のbyte[]に保存されています。Stream等を用いて画像データをストレージに書き出しています。

OverlayView.java

package com.ipentec.simplecamera;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.content.*;
import android.view.*;
import android.graphics.Canvas;
import android.graphics.Color;

public class OverlayView extends View {
  private Bitmap icon;
  int width;
  int height;

  public OverlayView(Context context) {
    super(context);
    setDrawingCacheEnabled(true);            
    icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
    setFocusable(true);
  }
  
  protected void onSizeChanged(int w, int h, int oldw, int oldh){
    //ビューのサイズを取得
    width= w;
   height= h;
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.TRANSPARENT);
    canvas.drawBitmap(icon, width-232,0, null);
  }
}
解説
オーバーレイのためのビューです。背景色を投下させる必要があるため、drawColorに"Color.TRANSPARENT"を指定する必要があります。

Preview.java

package com.ipentec.simplecamera;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import java.util.*;
import java.io.*;


public class Preview extends ViewGroup implements SurfaceHolder.Callback {
  private final String TAG = "Preview";
  SurfaceView mSurfaceView;
  SurfaceHolder mHolder;
  Size mPreviewSize;
  List<Size> mSupportedPreviewSizes;
  Camera mCamera;
  public Context pcontext;

  Preview(Context context) {
    super(context);
    mSurfaceView = new SurfaceView(context);
    pcontext = context;
      
    addView(mSurfaceView);
      	
    mHolder = mSurfaceView.getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  }
   
  public void setCamera(Camera camera) {
    mCamera = camera;
    if (mCamera != null) {
      mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
      requestLayout();
    }
  }
   
  public void switchCamera(Camera camera) { 
    setCamera(camera);
    try {     
      camera.setPreviewDisplay(mHolder);
    } catch (IOException exception) {
      Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    }
    Camera.Parameters parameters = camera.getParameters();
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    requestLayout(); 
    camera.setParameters(parameters);
  }
   
  public void surfaceCreated(SurfaceHolder holder) {
    try {
      if (mCamera != null) {
        mCamera.setPreviewDisplay(holder);
      }  
    }
    catch (IOException exception) {
      Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    }
  }
   
  public void surfaceDestroyed(SurfaceHolder holder) {
    if (mCamera != null) {
      mCamera.stopPreview(); 
    }
  }
   
  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {    
    //if (mCamera != null){
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    requestLayout();
    mCamera.setParameters(parameters);
    mCamera.startPreview();
    //}
  }
   
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
    setMeasuredDimension(width, height);
    if (mSupportedPreviewSizes != null) {
      mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
    }       
  }
   
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (changed && getChildCount() > 0) {
      final View child = getChildAt(0);
      final int width = r - l; 
      final int height = b - t;  
      int previewWidth = width;
      int previewHeight = height;
      if (mPreviewSize != null) { 
        previewWidth = mPreviewSize.width;
        previewHeight = mPreviewSize.height; 
      }
      // Center the child SurfaceView within the parent.
      if (width * previewHeight > height * previewWidth) {
        final int scaledChildWidth = previewWidth * height / previewHeight;
        child.layout((width - scaledChildWidth) / 2, 0,(width + scaledChildWidth) / 2, height);
      } else {
        final int scaledChildHeight = previewHeight * width / previewWidth;
        child.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2); 
      }
    } 
  }
   
  private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {   
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) w / h;
    if (sizes == null) return null;
    Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;
    int targetHeight = h;

    for (Size size : sizes) {
      double ratio = (double) size.width / size.height;
      if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
      if (Math.abs(size.height - targetHeight) < minDiff) {
        optimalSize = size;
        minDiff = Math.abs(size.height - targetHeight); 
      }
    }
    if (optimalSize == null) {
      minDiff = Double.MAX_VALUE; 
      for (Size size : sizes) {    
        if (Math.abs(size.height - targetHeight) < minDiff) { 
          optimalSize = size;
          minDiff = Math.abs(size.height - targetHeight);
        }
      }
    }
    return optimalSize;
  }
}
解説
カメラのプレビュー画像を表示するビューです。詳細はこちらの記事を参照してください。

実行結果

プロジェクトを実行します。下図の画面が表示され、カメラのプレビューが表示されます。左上の[撮影]ボタンを押すと撮影が行われ撮影結果の画像ファイルが保存されます。


保存された画像を確認します。画像はギャラリーアプリで閲覧できます。今回は"cmr"という名称のディレクトリに保存されます。ギャラリーから"cmr"フォルダを選択します。


撮影された画像が表示されます。


撮影結果が画像ファイルとして保存されていることが確認できました。

著者
iPentecのプログラマー、最近はAIの積極的な活用にも取り組み中。
とっても恥ずかしがり。
最終更新日: 2024-01-04
作成日: 2013-03-06
iPentec all rights reserverd.