Androidで簡単なカメラアプリを作る

test116_01.png
今回はシンプルなカメラアプリを作成する。

まずは雛形となるプロジェクトをEasyProjectGenerator for Androidで作る。今回はJavaのSimpleプロジェクトを利用した。

test116_02.png 自動生成されたプロジェクトをEclipseに取り込み、AndroidManifest.xmlにカメラ利用のパーミッション宣言を追加する。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.Test116"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Test116Act"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

</manifest>

test116_03.png
次にメインソースコードを編集する

。今回はメインの背景が緑色のレイアウトに1×1ドットのCameraViewを追加する形にした。CameraViewのサイズはCameraView内で変更することにする。

また総ショット数(画像保存時の連番に利用)を保存/利用するためにonSaveInstanceState()やonRestoreInstanceState()を作り、CameraViewへと渡すようにした。
package com.Test116;

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.graphics.Color;

public class Test116Act extends Activity
{
	private	CameraView	_view = null;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		LinearLayout layout = new LinearLayout(this);
		layout.setBackgroundColor(Color.GREEN);

		_view = new CameraView(this,savedInstanceState);

		layout.addView(_view,new LinearLayout.LayoutParams(1,1));
		setContentView(layout);
	}


	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);

		if(_view != null)
			_view.OnSaveState(outState);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState)
	{
		super.onRestoreInstanceState(savedInstanceState);
		
		if(_view != null)
			_view.OnLoadState(savedInstanceState);
	}
}

test116_04.png
そして「CameraView.java」ファイルを作成。CameraViewクラスを実装する。

プレビューはカメラがサポートする最小サイズで表示。プレビューを画面タッチすると撮影。撮影時はサポートする最大サイズでjpg保存。ファイル名は総ショット数を利用して決定している。


package com.Test116;

import android.hardware.Camera;
import android.content.Context;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.MotionEvent;
import java.io.FileOutputStream;
import java.io.File;

import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Media;
import android.util.Log;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import java.util.List;
import android.view.ViewGroup.LayoutParams;
import android.content.ContentValues;
import android.content.ContentResolver;


public class CameraView extends SurfaceView
	implements SurfaceHolder.Callback
{
	private final String	TAG = "Test116";
	
	private	SurfaceHolder	_SurfaceHolder;
	private	Camera			_Camera;
	private	int				_nReady;
	private	long			_nShotNo;


	public CameraView(Context context,Bundle savedInstanceState)
	{
		super(context);

		Log.d(TAG, "CameraView::CameraView()");

		_nReady = 0;
		_nShotNo = 0;
		OnLoadState(savedInstanceState);
	
		_SurfaceHolder = getHolder();
		_SurfaceHolder.addCallback(this);
		_SurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}

	//設定の保存
	public void OnSaveState(Bundle outState)
	{
		Log.d(TAG, "CameraView::OnSaveState()");

		if(outState != null)
		{
			outState.putLong("_nShotNo",_nShotNo);
		}
	}

	//設定の読込
	public void OnLoadState(Bundle savedInstanceState)
	{
		Log.d(TAG, "CameraView::OnLoadState()");

		if(savedInstanceState != null)
		{
			_nShotNo = savedInstanceState.getLong("_nShotNo");
		}
	}
	

	public void surfaceCreated(SurfaceHolder holder)
	{
		Log.d(TAG, "CameraView::surfaceCreated()");

		try
		{
			_Camera = Camera.open();
			_Camera.setPreviewDisplay(holder);
		}
		catch (Exception e)
		{
			Log.e(TAG, "CameraView::surfaceCreated() : Fail to setPreviewDisplay.");
		}
	}

	
	public void surfaceChanged(SurfaceHolder holder,int format,int w,int h)
	{
		Log.d(TAG, "CameraView::surfaceChanged()");

		Camera.Parameters	paramCamera = _Camera.getParameters();
		List<Size>			asizeSupport;
		Size		size;

		//カメラがサポートする撮影サイズ一覧を取得
		asizeSupport = paramCamera.getSupportedPictureSizes();
		if(asizeSupport.size() != 0)
		{
			//一番大きい撮影サイズを利用
			size = asizeSupport.get(0);
			paramCamera.setPictureSize(size.width,size.height);
		}
		
		//カメラがサポートするプレビューサイズ一覧を取得
		asizeSupport = paramCamera.getSupportedPreviewSizes();
		if(asizeSupport.size() != 0)
		{
			//一番小さいプレビューサイズを利用
			size = asizeSupport.get(asizeSupport.size() - 1);
			paramCamera.setPreviewSize(size.width,size.height);


			LayoutParams	paramLayout;

			//ビューサイズをプレビューサイズに合わせる
			paramLayout = getLayoutParams();
			paramLayout.width = size.width;
			paramLayout.height = size.height;
			setLayoutParams(paramLayout);
		}

		//カメラパラメーター設定
		_Camera.setParameters(paramCamera);
		
		_Camera.startPreview();		//プレビューの開始
		_nReady = 1;
	}

	
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		Log.d(TAG, "CameraView::surfaceDestroyed()");

		//カメラの破棄
		_nReady = 0;
		_Camera.setPreviewCallback(null);
		_Camera.stopPreview();
		_Camera.release();
		_Camera = null;
	}


	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		//タッチイベント取得
		if(event.getAction() == MotionEvent.ACTION_DOWN)
		{
			Log.d(TAG, "CameraView::onTouchEvent()");

			TakePicture();			//撮影
		}
		return true;
	}



	private ShutterCallback		_pfnShutterCallback = new ShutterCallback();

	private final class ShutterCallback implements android.hardware.Camera.ShutterCallback
	{
		public void onShutter()
		{
			Log.d(TAG, "CameraView::onShutter()");
		}
	};



	private RawPictureCallback	_pfnRawPictureCallback = new RawPictureCallback();
	
	private final class RawPictureCallback implements PictureCallback
	{
		public void onPictureTaken(byte [] rawData, android.hardware.Camera camera)
		{
			Log.d(TAG, "CameraView::onRawPictureTaken()");
		};
	}

	
	private final class JpgPictureCallback implements PictureCallback
	{
		public void onPictureTaken(byte [] data, android.hardware.Camera camera)
		{
			Log.d(TAG, "CameraView::onJpgPictureTaken()");

			String	strFolder;
			String	strFile;

			strFolder = Environment.getExternalStorageDirectory()+"/DCIM/Camera/";

			//保存ファイル名
			//重複チェックをしていないので、もしもすでにファイルがあったら上書きされる!
			strFile = strFolder + "test" + _nShotNo + ".jpg";
			
			//if(_nShotNo == 0)
			//{
			//	//フォルダ作成
			//	File	newFolder = new File(strFolder);
			//	newFolder.mkdir();
			//}

			try
			{
				//撮影画像保存(dataがすでにjpg画像になっているので、これをそのままファイルに落とすだけ)
				FileOutputStream	cFile = new FileOutputStream(strFile);
				cFile.write(data);
				cFile.close();

				//コンテンツ登録(androidギャラリーへの登録)
				long	nDate;
				ContentValues values = new ContentValues();

				nDate = System.currentTimeMillis();
				values.put(Images.Media.MIME_TYPE,"image/jpeg");			//必須
				values.put(Images.Media.DATA,strFile);						//必須:ファイルパス(uriからストリーム作るなら不要)
				values.put(Images.Media.SIZE,new File(strFile).length()); 	//必須:ファイルサイズ(同上)
//				values.put(Images.Media.TITLE,strFile);
//				values.put(Images.Media.DISPLAY_NAME,strFile);
				values.put(Images.Media.DATE_ADDED,nDate);
				values.put(Images.Media.DATE_TAKEN,nDate);
				values.put(Images.Media.DATE_MODIFIED,nDate);
//				values.put(Images.Media.DESCRIPTION,"");
//				values.put(Images.Media.LATITUDE,0.0);
//				values.put(Images.Media.LONGITUDE,0.0);
//				values.put(Images.Media.ORIENTATION,"");
				
				ContentResolver	contentResolver = getContext().getContentResolver();
				contentResolver.insert(Media.EXTERNAL_CONTENT_URI, values);
			}
			catch(Exception e)
			{
				Log.e(TAG, "CameraView::onJpgPictureTaken() : Fail to save image file.");
			}
			_nShotNo++;

			camera.startPreview();		//プレビュー再開
			_nReady = 1;
		};
	};


	
	public void TakePicture()
	{
		Log.d(TAG, "CameraView::takePicture()");
		
		if(_nReady != 0)
		{
			_nReady = 0;
			_Camera.takePicture(_pfnShutterCallback, _pfnRawPictureCallback,new JpgPictureCallback());
		}
	}
}

test116_05.png
実装が終わったので実行してみる。

test116_06.png
デバイスの選択画面で「Choose a running Android device」から実機を選択する。

test116_07.png
これで実機でアプリが実行されてプレビューが表示された。プレビュー画面を指でタッチすると撮影される。

test116_08.png
撮影した画像はAndroid標準のギャラリーアプリで確認できる。

test1.jpg
これが実際に撮影した画像を縮小したもの。保存サイズは2592×1952ドットでした。オートフォーカスを実装していないのでピンぼけすぎです。




プロジェクトファイルをダウンロード


カテゴリー「android」 のエントリー