Android 例項講解自定義Camera拍照和預覽以及前後置攝像頭切換

Android 例項講解自定義Camera拍照和預覽以及前後置攝像頭切換

上一篇博文講解了怎麼去呼叫本地圖片和呼叫系統拍照圖片(http://blog.csdn.net/a123demi/article/details/40003695)的功能。

而本博文將通過例項實現自定義Camera的功效。具體功能如下:

1.實現自定義Camera拍照;

2.實現前後置攝像頭的切換;

3.實現Camera拍照後圖片縮小顯示以及正常預覽;

4.實現Camera拍照後圖片儲存;

在具體實現程式碼之前,我們先來了解一下Android api對實現自定義Camera的介紹。

根據api的介紹,對於Camera應用可以簡單總結以下幾個步驟。

1.檢查Camera是否存在,並在AndroidManifest.xml中賦予相關的許可權;

2.建立一個繼承於SurfaceView並實現SurfaceHolder介面的Camera Preview類;

3.在2的基礎上新建一個Camera Preview佈局檔案;

4.設定一個拍照的監聽事件,例如單擊按鈕事件等;

5.實現拍照,並儲存拍照後的圖片到裝置;

6.釋放Camera,以方便其他應用可以使用。

下面將通過具體程式碼實現我們給出的三個功能。

一.相關的xml檔案

1.AndroidManifest.xml相關配置以及相關許可權,實現步驟一當中的許可權配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.camerasurfacedemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.camerasurfacedemo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.camerasurfacedemo.CameraActivity"
></activity>
<activity android:name="com.example.camerasurfacedemo.PreviewActivity"
></activity>
</application>
</manifest>

2.activity_main.xml主函式入口,進入自定義Camera介面入口,實現拍照後圖片縮小顯示功能

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
"
<Button
android:id="@ id/id_go_camera_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="進入拍照介面" />
<ImageView
android:id="@ id/id_show_camera_iv"
android:layout_width="150dp"
android:layout_height="200dp"
android:gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="拍照圖片顯示區域"
android:textColor="#FF0000"
android:textSize="20sp" />
</LinearLayout>

3.activity_camera.xml自定義Camera preview佈局,實現步驟2,該介面實現前後置攝像頭切換以及自定義拍照等

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="@ id/id_process_btns_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentTop="true"
>
<Button 
android:id="@ id/id_switch_camera_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="切換前後攝像頭"
/>
<Button 
android:id="@ id/id_capture_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="拍照"
/>
</LinearLayout>
<SurfaceView
android:id="@ id/id_area_sv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/id_process_btns_ll"
android:text="拍照區域" />
</RelativeLayout>

4.activity_preview.xml實現拍照後圖片放大預覽

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="拍照圖片預覽" 
android:textColor="#FF0000"
android:textSize="20sp"
/>
<ImageView
android:id="@ id/id_preview_camera_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>

二.java程式碼實現

1.幫助類HelpUtil.java

package com.example.camerasurfacedemo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
public class HelpUtil {
/**
* 根據圖片路徑獲取本地圖片的Bitmap
* 
* @param url
* @return
*/
public static Bitmap getBitmapByUrl(String url) {
FileInputStream fis = null;
Bitmap bitmap = null;
try {
fis = new FileInputStream(url);
bitmap = BitmapFactory.decodeStream(fis);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
bitmap = null;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fis = null;
}
}
return bitmap;
}
/**
* bitmap旋轉90度
* 
* @param bitmap
* @return
*/
public static Bitmap createRotateBitmap(Bitmap bitmap) {
if (bitmap != null) {
Matrix m = new Matrix();
try {
m.setRotate(90, bitmap.getWidth() / 2, bitmap.getHeight() / 2);// 90就是我們需要選擇的90度
Bitmap bmp2 = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), m, true);
bitmap.recycle();
bitmap = bmp2;
} catch (Exception ex) {
System.out.print("建立圖片失敗!"   ex);
}
}
return bitmap;
}
public static Bitmap getBitmapByUri(Uri uri,ContentResolver cr){
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(cr
.openInputStream(uri));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
bitmap = null;
}
return bitmap;
}
/**
* 獲取格式化日期字串
* @param date
* @return
*/
@SuppressLint("SimpleDateFormat")
public static String getDateFormatString(Date date) {
if (date == null)
date = new Date();
String formatStr = new String();
SimpleDateFormat matter = new SimpleDateFormat("yyyyMMdd_HHmmss");
formatStr = matter.format(date);
return formatStr;
}
}

2.主函式類MainActivity.java

package com.example.camerasurfacedemo;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity {
private Button goCameraBtn;
private ImageView showCameraIv;
private static final int CAMERA_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
goCameraBtn = (Button)this.findViewById(R.id.id_go_camera_btn);
goCameraBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
processGoCamera();
}
});
showCameraIv = (ImageView)this.findViewById(R.id.id_show_camera_iv);
showCameraIv.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
processShowCamera(v);
}
});
}
/**
* 處理進入camera事件
*/
private void processGoCamera(){
Intent intent = new Intent();
intent.setClass(this, CameraActivity.class);
startActivityForResult(intent,CAMERA_CODE);
}
/**
* 處理圖片跳轉進入預覽介面
*/
private void processShowCamera(View v){
Intent intent = new Intent();
intent.setClass(this, PreviewActivity.class);
/**
* 將圖片url傳給PreviewActivity
*/
intent.putExtra("cameraUrl", v.getContentDescription().toString());
startActivity(intent);
}
@Override
public void onActivityResult(int requestCode,int resultCode,Intent data){
super.onActivityResult(requestCode, resultCode, data);
if(RESULT_OK == resultCode){
if(CAMERA_CODE == requestCode){
/**
* 獲取activity返回的url
*/
Uri uri = data.getData();
String url = uri.toString().substring(uri.toString().indexOf("///") 2);
if(url != null && !TextUtils.isEmpty(url)){
showCameraIv.setContentDescription(url);
showCameraIv.setImageBitmap(HelpUtil.getBitmapByUrl(url));
}
}
}
}
}

注意:這裡通過startActivityForResult(intent,CAMERA_CODE)跳轉和 onActivityResult(int requestCode,int resultCode,Intent data)返回拍照後的圖片路徑資訊

3.自定義Camera preview類CameraActivity.java

package com.example.camerasurfacedemo;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
public class CameraActivity extends Activity implements OnClickListener,
SurfaceHolder.Callback {
private static final String TAG = CameraActivity.class.getSimpleName();
private static final int MEDIA_TYPE_IMAGE = 1;
private Button switchCameraBtn, captureBtn;
private SurfaceView surfaceSv;
private SurfaceHolder mHolder;
private Camera mCamera;
// 0表示後置,1表示前置
private int cameraPosition = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 不顯示標題
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_camera);
findById();
initData();
}
/**
* 初始化view
*/
private void findById() {
switchCameraBtn = (Button) this.findViewById(R.id.id_switch_camera_btn);
captureBtn = (Button) this.findViewById(R.id.id_capture_btn);
surfaceSv = (SurfaceView) this.findViewById(R.id.id_area_sv);
switchCameraBtn.setOnClickListener(this);
captureBtn.setOnClickListener(this);
}
/**
* 初始化相關data
*/
private void initData() {
// 獲得控制代碼
mHolder = surfaceSv.getHolder();
// 新增回撥
mHolder.addCallback(this);
}
@Override
public void onStart() {
super.onStart();
if (this.checkCameraHardware(this) && (mCamera == null)) {
// 開啟camera
mCamera = getCamera();
if (mHolder != null) {
setStartPreview(mCamera,mHolder);
}
}
}
private Camera getCamera() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception e) {
// Camera is not available (in use or does not exist)
camera = null;
Log.e(TAG, "Camera is not available (in use or does not exist)");
}
return camera;
}
@Override
public void onPause() {
super.onPause();
/**
* 記得釋放camera,方便其他應用呼叫
*/
releaseCamera();
}
@Override
public void onDestroy() {
super.onDestroy();
}
/**
* 釋放mCamera
*/
private void releaseCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();// 停掉原來攝像頭的預覽
mCamera.release();
mCamera = null;
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.id_switch_camera_btn:
// 切換前後攝像頭
int cameraCount = 0;
CameraInfo cameraInfo = new CameraInfo();
cameraCount = Camera.getNumberOfCameras();// 得到攝像頭的個數
for (int i = 0; i < cameraCount; i  ) {
Camera.getCameraInfo(i, cameraInfo);// 得到每一個攝像頭的資訊
if (cameraPosition == 1) {
// 現在是後置,變更為前置
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
/**
* 記得釋放camera,方便其他應用呼叫
*/
releaseCamera();
// 開啟當前選中的攝像頭
mCamera = Camera.open(i);
// 通過surfaceview顯示取景畫面
setStartPreview(mCamera,mHolder);
cameraPosition = 0;
break;
}
} else {
// 現在是前置, 變更為後置
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
/**
* 記得釋放camera,方便其他應用呼叫
*/
releaseCamera();
mCamera = Camera.open(i);
setStartPreview(mCamera,mHolder);
cameraPosition = 1;
break;
}
}
}
break;
case R.id.id_capture_btn:
// 拍照,設定相關引數
Camera.Parameters params = mCamera.getParameters();
params.setPictureFormat(ImageFormat.JPEG);
params.setPreviewSize(800, 400);
// 自動對焦
params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(params);
mCamera.takePicture(null, null, picture);
break;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
setStartPreview(mCamera,mHolder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
setStartPreview(mCamera,mHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 當surfaceview關閉時,關閉預覽並釋放資源
/**
* 記得釋放camera,方便其他應用呼叫
*/
releaseCamera();
holder = null;
surfaceSv = null;
}
/**
* 建立png圖片回撥資料物件
*/
PictureCallback picture = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
Log.d(TAG,
"Error creating media file, check storage permissions: ");
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
returnResult(pictureFile);
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: "   e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: "   e.getMessage());
}
}
};
/**
* Create a File for saving an image or video
*/
private static File getOutputMediaFile(int type) {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = HelpUtil.getDateFormatString(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath()   File.separator
"IMG_"   timeStamp   ".png");
} else {
return null;
}
return mediaFile;
}
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
/**
* activity返回式返回拍照圖片路徑
* @param mediaFile
*/
private void returnResult(File mediaFile) {
Intent intent = new Intent();
intent.setData(Uri.fromFile(mediaFile));
this.setResult(RESULT_OK, intent);
this.finish();
}
/**
* 設定camera顯示取景畫面,並預覽
* @param camera
*/
private void setStartPreview(Camera camera,SurfaceHolder holder){
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error starting camera preview: "   e.getMessage());
}
}
}

注意:

1.檢查Camera是否存在

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}

2.建立png格式的回撥介面

/**
* 建立png圖片回撥資料物件
*/
PictureCallback picture = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
Log.d(TAG,
"Error creating media file, check storage permissions: ");
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
returnResult(pictureFile);
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: "   e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: "   e.getMessage());
}
}
};

3.生成圖片檔案並儲存

/**
* Create a File for saving an image or video
*/
private static File getOutputMediaFile(int type) {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = HelpUtil.getDateFormatString(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath()   File.separator
"IMG_"   timeStamp   ".png");
} else {
return null;
}
return mediaFile;
}

4.Camera一定要release

	/**
* 釋放mCamera
*/
private void releaseCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();// 停掉原來攝像頭的預覽
mCamera.release();
mCamera = null;
}
}

5.Activity結果返回

	/**
* activity返回式返回拍照圖片路徑
* @param mediaFile
*/
private void returnResult(File mediaFile) {
Intent intent = new Intent();
intent.setData(Uri.fromFile(mediaFile));
this.setResult(RESULT_OK, intent);
this.finish();
}

4.圖片正常預覽PreviewActivity.java

package com.example.camerasurfacedemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Window;
import android.widget.ImageView;
import android.widget.Toast;
public class PreviewActivity extends Activity {
private ImageView previewCameraIv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_preview);
previewCameraIv = (ImageView)this.findViewById(R.id.id_preview_camera_iv);
Intent intent = this.getIntent();
String cameraUrl = intent.getStringExtra("cameraUrl").toString();
if(cameraUrl != null && !TextUtils.isEmpty(cameraUrl)){
previewCameraIv.setImageBitmap(HelpUtil.getBitmapByUrl(cameraUrl));
}else{
Toast.makeText(this, "圖片路徑錯誤", Toast.LENGTH_SHORT).show();
}
}
}

以上就是本博文的所有內容,謝謝品讀。

原始碼路徑:http://download.csdn.net/detail/a123demi/8029265