相机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开发笔记的完整目录