文章目录
1. 自定义预览相机SurfaceView
public class PreviewView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "PreviewView";
// 获得surfaceHolder引用
private SurfaceHolder holder;
public PreviewView(Context context) {
this(context, null);
}
public PreviewView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PreviewView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getScreenMetric(context);
initView();
}
private void initView() {
holder = getHolder();
holder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated this: " + Thread.currentThread().getName());
try {
// holder绑定相机预览
initCamera(holder);
} catch (Exception e) {
Log.i(TAG, e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "surfaceChanged");
if (mCamera != null) {
mCamera.startPreview();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed");
// 停止预览
mCamera.stopPreview();
// 释放相机资源
mCamera.release();
mCamera = null;
holder = null;
}
}
2. Camera关联SurfaceView的SurfaceHolder
public void initCamera(SurfaceHolder holder) {
// 遍历手机摄像头,默认设置为前置摄像头
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
// 得到摄像头的个数
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
mCamera = Camera.open(i);
}
}
Log.e(TAG, cameraId + "");
if (mCamera == null) {
mCamera = Camera.open();
}
try {
// 摄像头画面显示在Surface上
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
//设置参数,并拍照
setCameraParams(mCamera, mScreenWidth, mScreenHeight);
}
3. 预览尺寸(PreviewSize)
相机预览尺寸不能随意设置,贴出下面代码测试打印SurfaceView
private void setCameraParams(Camera camera, int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
// 获取摄像头支持的PreviewSize列表
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
// 打印所有支持的预览分辨率
for (Camera.Size size : previewSizeList) {
Log.i(TAG, "previewSizeList size.width=" + size.width + " size.height=" + size.height);
}
...
// 根据使用相机支持的预览列表,选取一个最合适的预览尺寸,这样相机预览就不会压缩变形
camera.setParameters(parameters);
}
只能通过Camera的getSupportedPreviewSizes 方法,获取支持的预览尺寸列表,并从列表中选择一个设置在parameters中,并且与展示的SurfaceView等比例,图像才不会显示变形
// 设置SurfaceView大小
PreviewView mPreviewView;
......
RelativeLayout.LayoutParams svParams = (RelativeLayout.LayoutParams) mPreviewView.getLayoutParams();
mPreviewView.height = camera.getParameters().getPreviewSize().width;
mPreviewView.width = camera.getParameters().getPreviewSize().height;
// 注意:surfaceview在重新设置宽高后,会调用到surfaceChanged方法
mPreviewView.setLayoutParams(svParams);
4. 图片尺寸(PictureSize)
Camera.Parameters parameters = mCamera.getParameters();
// 获取摄像头支持的PictureSize列表
List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
for (Camera.Size size : pictureSizeList) {
Log.i(TAG, "preview width: " + size.width + " height: " + size.height);
}
图片尺寸同样只能从支持的列表中选取一个设置。 设置后,调用Camera的takePicture方法获得拍照的图像数据大小就是设置大小
注意PictureSize和PreviewSize的宽高比也要保证一致,否则获取的图片会将Preview时的图像裁剪成PictureSize的比例
PreviewSize只会影响预览时的分辨率,不会影响获取图片的分辨率,所以Preview只是确定了图像的取景最大范围。最终图片的分辨率是由PreviewSize来决定
5. 设置SurfaceView预览尺寸
怎么样才能够达到适配多台手机,界面不产生拉伸变形
- 先将获取手机支持预览的尺寸列表通过方法
// 获取相机支持预览List<Size>
parmeters.getSupportedPreviewSizes()
- 先进行屏幕方向的一个判断,因为预览列表里面的尺寸都是w>h(即横屏),如果屏幕是竖屏则需要先将宽高进行调换,这样方便接下来的比较
/**
* 通过对比得到与宽高比最接近的尺寸(如果有相同尺寸,优先选择)
*
* @param surfaceWidth
* 需要被进行对比的原宽
* @param surfaceHeight
* 需要被进行对比的原高
* @param preSizeList
* 需要对比的预览尺寸列表
* @return 得到与原宽高比例最接近的尺寸
*/
protected Camera.Size getCloselyPreSize(int surfaceWidth, int surfaceHeight,
List<Size> preSizeList) {
int ReqTmpWidth;
int ReqTmpHeight;
// 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
if (mIsPortrait) {
ReqTmpWidth = surfaceHeight;
ReqTmpHeight = surfaceWidth;
} else {
ReqTmpWidth = surfaceWidth;
ReqTmpHeight = surfaceHeight;
}
//先查找preview中是否存在与surfaceview相同宽高的尺寸
for(Camera.Size size : preSizeList){
if((size.width == ReqTmpWidth) && (size.height == ReqTmpHeight)){
return size;
}
}
// 得到与传入的宽高比最接近的size
float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null;
for (Camera.Size size : preSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return retSize;
}
- 先用for循环将预览尺寸列表每个元素宽高与surfaceview的宽高进行比较,如果存在宽高尺寸都与surfaceview宽高尺寸相同的size则将该宽高设置为预览尺寸
- 如果步骤3找不到相同尺寸就得进行该步骤,将尺寸列表的宽高比例和surfaceview的比例作比较,找到一个相同或相近的。(一般来说,只要surfaceview的尺寸和屏幕尺寸相同,就可以找到相同的比例)然后将该尺寸的size设置为预览尺寸