首页 > 代码库 > 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


Android 实例讲解自定义Camera拍照和预览以及前后置摄像头切换