<b>Android Camera——拍照</b>
Camera定義在package android.hardware内,具體用法SDK裡叙述的可清楚了。架構解析什麼的網上也有很多,沒什麼必要講了(你認為我不知道我會說嗎)。
這篇呢,就整理了下Camera的拍照,其他還木有==
<b>一、系統相機</b>
<b> </b><b>1)調用方式</b>
系統相機的入口Action:MediaStore.ACTION_IMAGE_CAPTURE。隻需以startActivityForResult(…)啟動該Activity即可。
// 調用系統相機
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 1);
<b> 2</b><b>)處理方式</b>
在onActivityResult(…)中,處理傳回資訊。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (1 == requestCode) { // 系統相機傳回處理
if (resultCode == Activity.RESULT_OK) {
Bitmap cameraBitmap = (Bitmap) data.getExtras().get("data");
…… // 處理圖像
}
takeBtn1.setClickable(true);
}
super.onActivityResult(requestCode, resultCode, data);
}
<b>二、自定義相機</b>
<b> 1</b><b>)照相預覽</b>
繼承SufaceView寫自己的預覽界面,繼而放到你的照相Activity的布局裡。這裡面有個相機拍照監聽接口,用于在Activity裡再處理這些操作。<b></b>
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
/** LOG辨別 */
// private static final String TAG = "CameraPreview";
/** 分辨率 */
public static final int WIDTH = 1024;
public static final int HEIGHT = 768;
/** 監聽接口 */
private OnCameraStatusListener listener;
private SurfaceHolder holder;
private Camera camera;
// 建立一個PictureCallback對象,并實作其中的onPictureTaken方法
private PictureCallback pictureCallback = new PictureCallback() {
// 該方法用于處理拍攝後的照片資料
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// 停止照片拍攝
camera.stopPreview();
camera = null;
// 調用結束事件
if (null != listener) {
listener.onCameraStopped(data);
}
};
// Preview類的構造方法
public CameraPreview(Context context, AttributeSet attrs) {
super(context, attrs);
// 獲得SurfaceHolder對象
holder = getHolder();
// 指定用于捕捉拍照事件的SurfaceHolder.Callback對象
holder.addCallback(this);
// 設定SurfaceHolder對象的類型
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
// 在surface建立時激發
public void surfaceCreated(SurfaceHolder holder) {
// Log.e(TAG, "==surfaceCreated==");
// 獲得Camera對象
camera = Camera.open();
try {
// 設定用于顯示拍照攝像的SurfaceHolder對象
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
// 釋放手機攝像頭
camera.release();
// 在surface銷毀時激發
public void surfaceDestroyed(SurfaceHolder holder) {
// Log.e(TAG, "==surfaceDestroyed==");
// 釋放手機攝像頭
camera.release();
// 在surface的大小發生改變時激發
public void surfaceChanged(final SurfaceHolder holder, int format, int w,
int h) {
// Log.e(TAG, "==surfaceChanged==");
// 擷取照相機參數
Camera.Parameters parameters = camera.getParameters();
// 設定照片格式
parameters.setPictureFormat(PixelFormat.JPEG);
// 設定預浏尺寸
parameters.setPreviewSize(WIDTH, HEIGHT);
// 設定照片分辨率
parameters.setPictureSize(WIDTH, HEIGHT);
// 設定照相機參數
camera.setParameters(parameters);
// 開始拍照
camera.startPreview();
} catch (Exception e) {
// 停止拍照,并将拍攝的照片傳入PictureCallback接口的onPictureTaken方法
public void takePicture() {
// Log.e(TAG, "==takePicture==");
if (camera != null) {
// 自動對焦
camera.autoFocus(new AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (null != listener) {
listener.onAutoFocus(success);
}
// 自動對焦成功後才拍攝
if (success) {
camera.takePicture(null, null, pictureCallback);
}
});
// 設定監聽事件
public void setOnCameraStatusListener(OnCameraStatusListener listener) {
this.listener = listener;
/**
* 相機拍照監聽接口
*/
public interface OnCameraStatusListener {
// 相機拍照結束事件
void onCameraStopped(byte[] data);
// 拍攝時自動對焦事件
void onAutoFocus(boolean success);
<b> 2</b><b>)照相活動</b>
就是我們自己做的照相Activity了。完成後調用自己的相機,也就是跳轉入這個Activity。這裡面,照片以自定義路徑的形式存入的媒體庫。<b></b>
public class CameraActivity extends Activity implements
CameraPreview.OnCameraStatusListener {
public static final Uri IMAGE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
public static final String PATH = Environment.getExternalStorageDirectory()
.toString() + "/AndroidMedia/";
private CameraPreview mCameraPreview;
private ImageView focusView;
private boolean isTaking = false; // 拍照中
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 設定橫屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 設定全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 設定布局視圖
setContentView(R.layout.camera);
// 照相預覽界面
mCameraPreview = (CameraPreview) findViewById(R.id.preview);
mCameraPreview.setOnCameraStatusListener(this);
// 焦點圖檔
focusView = (ImageView) findViewById(R.id.focusView);
* 觸屏事件
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && !isTaking) {
isTaking = true;
mCameraPreview.takePicture();
return super.onTouchEvent(event);
* 存儲圖像并将資訊添加入媒體資料庫
private Uri insertImage(ContentResolver cr, String name, long dateTaken,
String directory, String filename, Bitmap source, byte[] jpegData) {
OutputStream outputStream = null;
String filePath = directory + filename;
File dir = new File(directory);
if (!dir.exists()) {
dir.mkdirs();
File file = new File(directory, filename);
if (file.createNewFile()) {
outputStream = new FileOutputStream(file);
if (source != null) {
source.compress(CompressFormat.JPEG, 75, outputStream);
} else {
outputStream.write(jpegData);
} catch (FileNotFoundException e) {
return null;
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Throwable t) {
ContentValues values = new ContentValues(7);
values.put(MediaStore.Images.Media.TITLE, name);
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.DATA, filePath);
return cr.insert(IMAGE_URI, values);
* 相機拍照結束事件
public void onCameraStopped(byte[] data) {
Log.e("onCameraStopped", "==onCameraStopped==");
// 建立圖像
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
// 系統時間
long dateTaken = System.currentTimeMillis();
// 圖像名稱
String filename = DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken)
.toString() + ".jpg";
// 存儲圖像(PATH目錄)
Uri uri = insertImage(getContentResolver(), filename, dateTaken, PATH,
filename, bitmap, data);
// 傳回結果
Intent intent = getIntent();
intent.putExtra("uriStr", uri.toString());
intent.putExtra("dateTaken", dateTaken);
// intent.putExtra("filePath", PATH + filename);
// intent.putExtra("orientation", orientation); // 拍攝方向
setResult(20, intent);
// 關閉目前Activity
finish();
* 拍攝時自動對焦事件
public void onAutoFocus(boolean success) {
// 改變對焦狀态圖像
if (success) {
focusView.setImageResource(R.drawable.focus2);
} else {
focusView.setImageResource(R.drawable.focus1);
Toast.makeText(this, "焦距不準,請重拍!", Toast.LENGTH_SHORT).show();
isTaking = false;
<b>3</b><b>)相機調用&</b><b>處理</b>
// 調用自定義相機
Intent intent = new Intent(this, CameraActivity.class);
startActivityForResult(intent, 2);
if (2 == requestCode) { // 自定義相機傳回處理
// 拍照成功後,響應碼是20
if (resultCode == 20) {
Bundle bundle = data.getExtras();
// 獲得照片uri
Uri uri = Uri.parse(bundle.getString("uriStr"));
// 獲得拍照時間
long dateTaken = bundle.getLong("dateTaken");
try {
// 從媒體資料庫擷取該照片
Bitmap cameraBitmap = MediaStore.Images.Media.getBitmap(
getContentResolver(), uri);
previewBitmap(cameraBitmap); // 預覽圖像
// 從媒體資料庫删除該照片(按拍照時間)
getContentResolver().delete(
CameraActivity.IMAGE_URI,
MediaStore.Images.Media.DATE_TAKEN + "="
+ String.valueOf(dateTaken), null);
} catch (Exception e) {
e.printStackTrace();
takeBtn2.setClickable(true);
<b>三、我加的相機蒙版</b><b>^^</b>
這個最重要了,就是為了這個才把這還未完工的工程放上來的。
布局檔案裡面我們在加一個自定義的MaskSurfaceView,注意放在相機預覽的前面,并要設定成透明(包括重刷圖層的時候也要注意透明度)。
<?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:gravity="center"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" >
<org.join.meida.camera.takephoto.MaskSurfaceView
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<org.join.meida.camera.takephoto.CameraPreview
android:id="@+id/preview"
<ImageView
android:id="@+id/focusView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/focus1" />
</RelativeLayout>
</LinearLayout>
蒙版來啦,注意到心了沒有?(3d愛心是網上别人實作的)<b></b>
public class MaskSurfaceView extends SurfaceView implements
SurfaceHolder.Callback, Runnable {
// 定義SurfaceHolder對象
private SurfaceHolder mSurfaceHolder;
// 循環标記
private boolean loop = true;
// 循環間隔
private static final long TIME = 300;
// 計數器
private int mCount;
// 繪制方式
private int mode;
public MaskSurfaceView(Context context, AttributeSet attrs) {
mSurfaceHolder = getHolder(); // 擷取SurfaceHolder
mSurfaceHolder.addCallback(this); // 添加回調
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT); // 設定透明
mode = new Random().nextInt(2); // 随機一個[0-2)數
new Thread(this).start(); // 開始繪制
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
loop = false;
public void run() {
while (loop) {
Thread.sleep(TIME);
} catch (InterruptedException e) {
synchronized (mSurfaceHolder) {
drawMask();
* 繪制蒙版
private void drawMask() {
// 鎖定畫布,得到canvas
Canvas mCanvas = mSurfaceHolder.lockCanvas();
// 避免surface銷毀後,線程喚醒仍進入繪制
if (mSurfaceHolder == null || mCanvas == null)
return;
int w = mCanvas.getWidth();
int h = mCanvas.getHeight();
/* 建立一個畫筆 */
Paint mPaint = new Paint();
mPaint.setAntiAlias(true); // 設定抗鋸齒
mPaint.setColor(0x00000000); // 設定透明黑色
mCanvas.drawRect(0, 0, w, h, mPaint); // 重繪背景
setPaintColor(mPaint); // 循環設定畫筆顔色
mPaint.setStyle(Paint.Style.STROKE); // 描邊
if (0 == mode) {
drawHeart2D(mCanvas, mPaint, w / 2, h / 2, h / 2); // 畫一個2d愛心
drawHeart3D(mCanvas, mPaint); // 畫一個3d愛心
// 繪制後解鎖,繪制後必須解鎖才能顯示
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
/** 畫一個2d愛心(半圓+sin曲線) */
private void drawHeart2D(Canvas mCanvas, Paint mPaint, int centerX,
int centerY, float height) {
float r = height / 4;
/* 心兩半圓結點處 */
float topX = centerX;
float topY = centerY - r;
/* 左上半圓 */
RectF leftOval = new RectF(topX - 2 * r, topY - r, topX, topY + r);
mCanvas.drawArc(leftOval, 180f, 180f, false, mPaint);
/* 右上半圓 */
RectF rightOval = new RectF(topX, topY - r, topX + 2 * r, topY + r);
mCanvas.drawArc(rightOval, 180f, 180f, false, mPaint);
/* 下半兩sin曲線 */
float base = 3 * r;
double argu = Math.PI / 2 / base;
float y = base, value;
while (y >= 0) {
value = (float) (2 * r * Math.sin(argu * (base - y)));
mCanvas.drawPoint(topX - value, topY + y, mPaint);
mCanvas.drawPoint(topX + value, topY + y, mPaint);
y -= 1;
// 1)心形函數:x²+(y-³√x²)²=1
// >> x^2+(y-(x^2)^(1/3))^2=1
//
// 2)心形的各種畫法:
// >> http://woshao.com/article/1a855728bda511e0b40e000c29fa3b3a/
// 3)笛卡爾情書的秘密——心形函數的繪制
// >> http://www.cssass.com/blog/index.php/2010/808.html
/** 畫一個3d愛心 */
private void drawHeart3D(Canvas mCanvas, Paint mPaint) {
/* 畫一個3d愛心 */
int i, j;
double x, y, r;
for (i = 0; i <= 90; i++) {
for (j = 0; j <= 90; j++) {
r = Math.PI / 45 * i * (1 - Math.sin(Math.PI / 45 * j)) * 20;
x = r * Math.cos(Math.PI / 45 * j) * Math.sin(Math.PI / 45 * i)
+ w / 2;
y = -r * Math.sin(Math.PI / 45 * j) + h / 4;
mCanvas.drawPoint((float) x, (float) y, mPaint);
/** 循環設定畫筆顔色 */
private void setPaintColor(Paint mPaint) {
mCount = mCount < 100 ? mCount + 1 : 0;
switch (mCount % 6) {
case 0:
mPaint.setColor(Color.BLUE);
break;
case 1:
mPaint.setColor(Color.GREEN);
case 2:
mPaint.setColor(Color.RED);
case 3:
mPaint.setColor(Color.YELLOW);
case 4:
mPaint.setColor(Color.argb(255, 255, 181, 216));
case 5:
mPaint.setColor(Color.argb(255, 0, 255, 255));
default:
mPaint.setColor(Color.WHITE);
<b>四、後記</b>
今天是幾号來着?T^T。
<a href="http://down.51cto.com/data/2359820" target="_blank">附件:http://down.51cto.com/data/2359820</a>
本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/779942,如需轉載請自行聯系原作者