Androidアプリでカメラ機能を利用する方法のひとつに、撮影するためのActivityの自作があります。
プログラムを組むときにちょっとややこしかったので、メモ代わりに書いておこうと思います。
・パーミッション設定
<uses-permission android:name=”android.permission.CAMERA” />
カメラ機能を有効にするためのパーミッションです。
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />
撮影した画像をSDカードに保存するためのパーミッションです。
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
SDカードが端末に入っているかどうかを判別するためのパーミッションです。
・Manifestにアクティビティ追加
<activity
android:name=”CameraActivity”
android:theme=”@android:style/Theme.Dialog”
android:exported=”false”
android:screenOrientation=”landscape”>
</activity>
注目してほしいのは、
android:theme=”@android:style/Theme.Dialog”
の記述です。
これを書き加えることで、アクティビティをダイアログの画面風にすることができます。
・レイアウトxmlを作成
lo_camera.xml
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:background=”@color/black”
android:orientation=”vertical” >
<LinearLayout
android:orientation=”vertical”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center”
android:gravity=”center”>
<SurfaceView
android:id=”@+id/surface_view”
android:layout_gravity=”center”
android:gravity=”center”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />
</LinearLayout>
</LinearLayout>
・カメラ機能を呼び出すときの記述
遷移元Activity
// SDカードのチェック
String status = Environment.getExternalStorageState();
if (!status.equals(Environment.MEDIA_MOUNTED)) {
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(
[遷移元Activity名].this);
builder.setMessage("データを保存するにはSDカードが必要です")
.setPositiveButton("OK", null).show();
} else {
Intent intent = new Intent(getApplicationContext(),
CameraActivity.class);
intent.setAction(Intent.ACTION_VIEW);
startActivityForResult(intent, 1001);
}
カメラ機能を呼び出すタイミングでSDカードの有無をチェックしています。
もしSDカードが端末にセットされていなければエラーダイアログを表示して、セットされていればカメラアクティビティをintentで呼び出しています。
・カメラ機能のプログラム
CameraActivity
import java.io.File;
import java.io.FileOutputStream;
import android.app.Activity;
import android.content.Intent;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.Toast;
public class CameraActivity extends Activity {
private SurfaceView mySurfaceView;
private Camera myCamera;
private SurfaceHolder.Callback mSurfaceListener =
new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
myCamera = Camera.open();
try {
myCamera.setPreviewDisplay(holder);
} catch (Exception e) {
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
myCamera.release();
myCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
Camera.Parameters parameters = myCamera.getParameters();
android.view.ViewGroup.LayoutParams lp = mySurfaceView.getLayoutParams();
int ch = parameters.getPreviewSize().height;
int cw = parameters.getPreviewSize().width;
if(ch/cw > height/width){
lp.width = width;
lp.height = width*ch/cw;
}else{
lp.width = height*cw/ch;
lp.height = height;
}
mySurfaceView.setLayoutParams( lp );
myCamera.setParameters(parameters);
myCamera.startPreview();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lo_camera);
Toast.makeText(CameraActivity.this,
"画面をタッチすると撮影します",
Toast.LENGTH_LONG).show();
Window myWin = getWindow(); //現在の表示されているウィンドウを取得
LayoutParams lp = new LayoutParams(); //LayoutParams作成
lp.screenBrightness = 1.0f; //輝度最大
lp.width = LayoutParams.WRAP_CONTENT;
lp.height = LayoutParams.WRAP_CONTENT;
myWin.setAttributes(lp); //ウィンドウにLayoutParamsを設定
mySurfaceView = (SurfaceView)findViewById(R.id.surface_view);
SurfaceHolder holder = mySurfaceView.getHolder();
holder.addCallback(mSurfaceListener);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
// シャッターが押されたときに呼ばれるコールバック
private Camera.ShutterCallback mShutterListener =
new Camera.ShutterCallback() {
public void onShutter() {
// TODO Auto-generated method stub
}
};
// JPEGイメージ生成後に呼ばれるコールバック
private Camera.PictureCallback mPictureListener =
new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
camera.startPreview();
// SDカードにJPEGデータを保存する
if (data != null) {
try {
//「/sdcard」を取得
File directory = Environment.getExternalStorageDirectory();
//SDカードに書き込める状態かチェック
if (directory.exists()){
//⇒書き込める状態の場合
if(directory.canWrite()){
//画像を保存するフォルダを作成します。
File file1 = new File(directory.getAbsolutePath() + "/[保存したいフォルダ名]");
file1.mkdir();
File file2 = new File(directory.getAbsolutePath() + "/[保存したいフォルダ名]/" + getPackageName());
file2.mkdir();
}
}
//フルパスを取得
String folderpath = directory.getAbsolutePath() + "/[保存したいフォルダ名]/" + getPackageName() + "/";
// sdcardフォルダを指定
File root = new File(folderpath);
// 日付でファイル名を作成
String filename = System.currentTimeMillis() + ".jpg";
// 保存処理開始
FileOutputStream fos = null;
fos = new FileOutputStream(new File(root, filename));
// jpegで保存
//capturedImage.compress(CompressFormat.JPEG, 100, fos);
fos.write(data);
// 保存処理終了
fos.close();
//データを作成
Intent i_data = new Intent();
i_data.putExtra("FOLDER_PATH", folderpath);
i_data.putExtra("FILE_NAME", filename);
//結果を設定
setResult(RESULT_OK, i_data);
//画面終了して遷移元に戻る
finish();
} catch (Exception e) {
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(CameraActivity.this);
builder.setMessage("撮影した画像の保存に失敗しました").setPositiveButton("OK", null).show();
}
//camera.startPreview();
}
}
};
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (myCamera != null) {
myCamera.takePicture(mShutterListener, null, mPictureListener);
}
}
return true;
}
}
このソースの通りなので詳しい解説は書きませんが、
Window myWin = getWindow(); //現在の表示されているウィンドウを取得
LayoutParams lp = new LayoutParams(); //LayoutParams作成
lp.screenBrightness = 1.0f; //輝度最大
lp.width = LayoutParams.WRAP_CONTENT;
lp.height = LayoutParams.WRAP_CONTENT;
myWin.setAttributes(lp); //ウィンドウにLayoutParamsを設定
この部分はなぜ必要か解りづらいと思います。
試しにこの記述を外して動作させてみてください。画面が暗くなってしまうはずです。
アクティビティをダイアログ風に表示させているので、まわりの暗くしている画面に明るさが連動してしまっているようです。
LayoutParamsで明るさを明示的に設定しています。
・撮影した画像のパスやファイル名を受け取る
遷移元Activity
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
// キャンセルボタンで戻ってきたときはRESULT_CANCELEDが入る
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 1001:
if (intent != null) {
// 処理:値を取得
Bundle extras = intent.getExtras();
String sValue1 = extras.getString("FOLDER_PATH");
String sValue2 = extras.getString("FILE_NAME");
}
break;
default:
break;
}
}
}
CameraActivity を
startActivityForResult(intent, 1001);
で呼び出したので、finish()するとonActivityResult()に戻ってきます。
この記述で撮影した画像のパスとファイル名を受け取れるので、あとはその変数を煮るなり焼くなりお好きにしてください。
これを実装すると、こんな感じのカメラビューが表示されると思います。

スクリーンショットではカメラのプレビューが真っ暗ですが、本来ならここにファインダーから覗いた視界が映っています。
最近配布されているAndroidアプリのカメラ機能の実装のされ方を見るに、こういった自作のカメラ機能はあまり使われていないようですが、この記事がどなたかの一助になれば幸いです。