Android开发笔记(五十六)摄像头拍照

相机Camera

Camera是直接操作摄像头硬件的工具类。常用的方法如下:

getNumberOfCameras : 获取本机的摄像头数目
open : 打开摄像头,默认打开后置摄像头。如果有多个摄像头,那么open(0)表示打开后置摄像头,open(1)表示打开前置摄像头。
以上两个方法是静态方法。

getParameters : 获取摄像头的拍照参数。
setParameters : 设置摄像头的拍照参数。
--setPreviewSize : 设置预览界面的尺寸。
--setPictureSize : 设置保存图片的尺寸。
--setPictureFormat : 设置图片格式。一般使用ImageFormat.JPEG表示jpg格式,
--setFocusMode : 设置对焦模式。一般使用FOCUS_MODE_AUTO表示自动对焦。
以上是Camera.Parameters的参数设置方法。

setPreviewDisplay : 设置预览界面,参数为SurfaceHolder类型。
startPreview : 开始预览。该方法在setPreviewDisplay之后调用。
stopPreview : 停止预览
unlock : 录像时需要对摄像头解锁,这样摄像头才能持续录像。该方法在startPreview之后调用。
lock : 录像完毕对摄像头加锁。该方法在stopPreview之后调用。
setDisplayOrientation : 设置预览的角度。因为Android的0角度都在三点钟位置,而手机画面都是六点钟的垂直位置,所以从三点钟到六点钟需要旋转90度。
autoFocus : 设置对焦事件,参数为AutoFocusCallback类型。比如说在对焦成功时显示一个图片提示用户可以拍照了。
takePicture : 拍照。第一个参数ShutterCallback用来控制按下快门时的事件,我们可在此播放拍照声音,默认就是咔嚓一声;后面的几个回调接口PictureCallback分别对应原始图像、缩放和压缩图像和JPG图像,图像数据可以在接口中的onPictureTaken方法中获得,通常我们只关心最后一个的JPG图像数据,所以前面的接口参数可以直接传null。
release : 释放摄像头。每次退出拍照都要释放,因为摄像头不能重复打开,要么就是把Camera对象做成单例模式。


预览视图SurfaceView/预览持有者SurfaceHolder

SurfaceView是Android中的一种特殊视图,它拥有独立的绘图表面,即它不与其宿主页面共享同一个绘图表面。由于拥有独立的绘图表面,因此SurfaceView的界面就可以在一个独立的线程中进行绘制,我们称之为渲染线程。因为它不占用主线程资源,所以一方面可以实现复杂而高效的UI,另一方面也会及时响应用户输入。鉴于SurfaceView具备如上特性,故而它可用于拍照以及录像的预览界面,也可用于游戏的画面。


不过SurfaceView自身主要完成绘图功能,其他功能设置以及事件处理还有待于SurfaceHolder来操作。SurfaceView的getHolder方法把二者关联了起来,可获取预览界面当前干活的操纵者。SurfaceHolder应与SurfaceView配合使用,下面是SurfaceHolder的常用方法:
addCallback : 添加回调接口
removeCallback : 移除回调接口
isCreating : 判断预览界面是否有效
setFormat : 设置预览格式。PixelFormat.TRANSPARENT表示透明,PixelFormat.TRANSLUCENT表示半透明,PixelFormat.OPAQUE表示不透明。
setFixedSize : 设置预览界面的尺寸
setSizeFromLayout : 设置预览界面的尺寸为布局文件中的配置
getSurfaceFrame : 获取预览界面的尺寸
getSurface : 获取预览视图的对象。主要用于播放视频。


拍照的相关事件

下面是几个拍照用到的回调事件接口:

预览变化事件 
监听器类名 : SurfaceHolder.Callback
设置监听器的方法 : 
Camera.setPreviewDisplay : 添加预览持有者SurfaceHolder。该方法用于关联Camera和SurfaceHolder
SurfaceHolder.addCallback : 添加回调接口Callback。该方法用于关联SurfaceView和SurfaceHolder,它与Camera.setPreviewDisplay最终联合完成SurfaceView与Camera的关联,即摄像头的画面展示在预览界面上。
SurfaceHolder.removeCallback : 移除回调接口Callback
监听器需要重写的方法 : 
surfaceCreated : 预览创建。不管是拍照还是录像,通常在该方法中设置拍照预览Camera.setPreviewDisplay。
surfaceChanged : 预览变化
surfaceDestroyed : 预览结束。注意SurfaceView的渲染线程只在surfaceCreated和surfaceDestroyed之间有效,所以如果在别处操作SurfaceView画面,得判断当前预览界面是否有效,也就是调用SurfaceHolder.isCreating方法来判断。

自动对焦事件 
监听器类名 : Camera.AutoFocusCallback
设置监听器的方法 : Camera.autoFocus
监听器需要重写的方法 : onAutoFocus

快门按下事件 
监听器类名 : Camera.ShutterCallback
设置监听器的方法 : Camera.takePicture
监听器需要重写的方法 : onShutter

拍照事件 
监听器类名 : Camera.PictureCallback
设置监听器的方法 : Camera.takePicture
监听器需要重写的方法 : onPictureTaken

变焦事件 
监听器类名 : Camera.OnZoomChangeListener
设置监听器的方法 : Camera.setZoomChangeListener
监听器需要重写的方法 : onZoomChange


扫描二维码

这个功能最有名的应用就是微信里的“扫一扫”了,通过摄像头拍照从二维码中获取相关信息,然后再进行相应操作(比如说添加好友、下载文件、访问页面等等)。Android中的二维码扫描可用Google的zxing开源库,再结合zxing的使用框架MipcaActivityCapture。
下面是zxing+MipcaActivityCapture框架的代码集成例子:
1、给工程加入zxing3.2.1.jar;
2、把MipcaActivityCapture源码(com.app.zxing)加入到工程;
3、编写MipcaActivityCapture的布局文件activity_capture.xml,主要是加入SurfaceView和com.app.zxing.view.ViewfinderView两个视图,前一个视图是预览界面,后一个是扫码界面;
4、如果需要调整扫描界面的UI,则修改ViewfinderView的onDraw方法,可加入新的元素或者调整尺寸。
5、对扫码结果的处理见MipcaActivityCapture的handleDecode方法,视情况做相应处理,如添加好友、下载文件、访问页面等等。


代码示例

下面是相机视图CameraView的代码示例:
import com.example.exmcamera.util.BitmapUtil;
import com.example.exmcamera.util.MetricsUtil;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
	private static final String TAG = "CameraView";
	private Context mContext;
	private Camera mCamera;
    private Bitmap mBitmap = null;
	private SurfaceHolder mHolder = null;
	private boolean isPreviewing = false;
	private Point mCameraSize;
	private int mCameraType = CAMERA_BEHIND;
	public static int CAMERA_BEHIND = 0;
	public static int CAMERA_FRONT = 1;

	public CameraView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		mHolder = getHolder();
		mHolder.setFormat(PixelFormat.TRANSPARENT);//translucent半透明 transparent透明
		//mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		mHolder.addCallback(this);
	}

	public CameraView(Context context) {
		this(context, null);
	}

	public int getCameraType() {
		return mCameraType;
	}
	
	public void setCameraType(int CameraType) {
		mCameraType = CameraType;
	}

	public Bitmap getPhoto() {
		if (mBitmap != null) {
			Log.d(TAG, "mBitmap.size="+(mBitmap.getByteCount()/1024)+"K");
		} else {
			Log.d(TAG, "mBitmap is null.");
		}
		return mBitmap;
	}

	public void doTakePicture() {
		if(isPreviewing && (mCamera != null)) {
			mCamera.takePicture(mShutterCallback, null, mPictureCallback);
		}
	}

	//快门按下的回调,在这里我们可以设置类似播放“咔嚓”声之类的操作。默认的就是咔嚓。
	ShutterCallback mShutterCallback = new ShutterCallback() {
		public void onShutter() {
			Log.d(TAG, "myShutterCallback:onShutter...");
		}
	};
	
	PictureCallback mPictureCallback = new PictureCallback() {
		public void onPictureTaken(byte[] data, Camera camera) {
			Log.d(TAG, "mPictureCallback:onPictureTaken...");
			Bitmap b = null;
			if(null != data) {
				b = BitmapFactory.decodeByteArray(data, 0, data.length);//data是字节数据,将其解析成位图
				mCamera.stopPreview();
				isPreviewing = false;
			}
			if (mCameraType == CameraView.CAMERA_BEHIND) {
				mBitmap = BitmapUtil.getRotateBitmap(b, 90);
			} else {
				mBitmap = BitmapUtil.getRotateBitmap(b, -90);
			}
			Log.d(TAG, "mBitmap.size="+(mBitmap.getByteCount()/1024)+"K");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//再次进入预览
			mCamera.startPreview();
			isPreviewing = true;
		}
	};

	@Override
	public void surfaceCreated(SurfaceHolder mHolder) {
		// 当预览视图创建的时候开启相机
		mCamera = Camera.open(mCameraType);
		try {
			// 设置预览
			mCamera.setPreviewDisplay(mHolder);
			mCameraSize = MetricsUtil.getCameraSize(mCamera.getParameters(), MetricsUtil.getSize(mContext));
			Log.d(TAG, "width="+mCameraSize.x+", height="+mCameraSize.y);
		    Camera.Parameters parameters = mCamera.getParameters();
			// 设置预览大小
		    parameters.setPreviewSize(mCameraSize.x, mCameraSize.y);
			// 设置图片保存时的分辨率大小
			parameters.setPictureSize(mCameraSize.x, mCameraSize.y);
			// 设置格式
			parameters.setPictureFormat(ImageFormat.JPEG);
			// 设置自动对焦。前置摄像头似乎无法自动对焦
			if (mCameraType == CameraView.CAMERA_BEHIND) {
				parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
			}
		    mCamera.setParameters(parameters);
		} catch (Exception e) {
			Log.d(TAG, "setPreviewDisplay error: "+e.getMessage());
			// 释放相机资源并置空
			mCamera.release();
			mCamera = null;
		}
		return;
	}

	@Override
	public void surfaceChanged(SurfaceHolder mHolder, int format, int width, int height) {
		Log.d(TAG, "surfaceChanged");
		mCamera.setDisplayOrientation(90);
		// 开始预览
		mCamera.startPreview();
		isPreviewing = true;
		mCamera.autoFocus(null);
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder mHolder) {
		Log.d(TAG, "surfaceDestroyed");
		mCamera.stopPreview();
		mCamera.release();
		mCamera = null;
	}

}


下面是拍照页面CameraActivity的代码示例:
import java.text.SimpleDateFormat;
import java.util.Date;

import com.example.exmcamera.R;
import com.example.exmcamera.util.BitmapUtil;
import com.example.exmcamera.util.MetricsUtil;
import com.example.exmcamera.widget.CameraView;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageButton;

public class CameraActivity extends Activity implements OnClickListener {
	private static final String TAG = "CameraActivity";
	private CameraView cameraView;
	private ImageButton shutterBtn;
	private int mCameraType;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.item_camera);
		mCameraType = getIntent().getIntExtra("type", CameraView.CAMERA_BEHIND);
		cameraView = (CameraView)findViewById(R.id.camera_view);
		cameraView.setCameraType(mCameraType);
		shutterBtn = (ImageButton)findViewById(R.id.btn_shutter);
		shutterBtn.setOnClickListener(this);
		initViewParams();
	}

	private void initViewParams(){
		LayoutParams paramsLayout = cameraView.getLayoutParams();
		Point size = MetricsUtil.getSize(this);
		paramsLayout.width = size.x;
		paramsLayout.height = size.y;
		cameraView.setLayoutParams(paramsLayout);

		LayoutParams paramsButton = shutterBtn.getLayoutParams();
		paramsButton.width = MetricsUtil.dip2px(this, 60);
		paramsButton.height = MetricsUtil.dip2px(this, 60);
		shutterBtn.setLayoutParams(paramsButton);
	}

	@Override
	public void onBackPressed() {
        Intent intent = new Intent();
		Bundle bundle = new Bundle();
		Bitmap bitmap = cameraView.getPhoto();
		if (bitmap == null) {
			bundle.putString("is_null", "yes");
		} else {
			bundle.putString("is_null", "no");
			String path = String.format("%s%s.jpg", BitmapUtil.getCachePath(this),getNowDateTime());
			BitmapUtil.saveFile(cameraView.getPhoto(), path);
			bundle.putString("path", path);
		}
		intent.putExtras(bundle);
        setResult(Activity.RESULT_OK, intent);
        finish();
	}

	private String getNowDateTime() {
		SimpleDateFormat s_format = new SimpleDateFormat("yyyyMMddHHmmss");
		Date d_date = new Date();
		String s_date = "";
		s_date = s_format.format(d_date);
		return s_date;
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_shutter) {
			cameraView.doTakePicture();
		}
	}

}





点此查看Android开发笔记的完整目录

你可能感兴趣的:(android,二维码,Camera,SurfaceView,SurfaceHolder)