<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">经常在项目中有这样的需求:预览并拍摄照片, 照片的比例为w/h=4/3,并在预览页面中有取景框。 取景框通常为正方形或者长方形, 正方形一般为标记或者条码扫描,长方形一般为卡片类,如身份证、银行卡等。</span>
对于这类需求主要在与对相机的控制。常见的有3个大小: previewSize, pictureSize, 和surfaceViewSize,这三个大小必须在比例上一致方可使得拍摄和预览的图片不发生变形问题。 要获取合适大小的比例可以通过以下代码来获取
if (camera == null){
try {
camera = Camera.open();
}catch (Exception e){
event.setStatus(BaseEvent.EVENT_STATUS.EVENT_ERROR);
event.setMsg("相机不可用,请保证应用可以访问相机");
EventBus.getDefault().post(event);
return;
}
if(camera == null){//提醒
event.setStatus(BaseEvent.EVENT_STATUS.EVENT_ERROR);
event.setMsg("相机不可用,请保证应用可以访问相机");
EventBus.getDefault().post(event);
return;
}
}
//相机参数设置
parameters = camera.getParameters();
parameters.setPictureFormat(PixelFormat.JPEG);//设置拍照后存储的图片格式
parameters.setRotation(90);
camera.setDisplayOrientation(90);
Camera.Size presize = getFitSize(parameters.getSupportedPreviewSizes(), 1.3f, 640);
Camera.Size picsize = getFitSize(parameters.getSupportedPictureSizes(), 1.3f, 640);
if (presize == null || picsize == null){
camera.release();
camera = null;
event.setStatus(BaseEvent.EVENT_STATUS.EVENT_ERROR);
event.setMsg("相机格式不支持");
EventBus.getDefault().post(event);
return;
}
parameters.setPreviewSize(presize.width, presize.height);
parameters.setPictureSize(picsize.width, picsize.height);
camera.setParameters(parameters);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
e.printStackTrace();
camera.release();
camera=null;
}
/**获取适配大小
* */
protected Camera.Size getFitSize(List<Camera.Size> list, float rate, int width){
sort(list);
Camera.Size tmp = null;
for (Camera.Size size:list){
float sub = Math.abs(size.width*1.0f/size.height - rate);
if ( sub < 0.039 && sub > 0.031 && size.width == width){
return size;
}else if (sub < 0.039 && sub > 0.031 ){
tmp = size;
}
}
return tmp;
}
private void sort(List<Camera.Size> list){
Collections.sort(list, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
if (lhs.width>rhs.width)
return 1;
else if (lhs.width < rhs.width)
return -1;
return 0;
}
});
}
在上述代码中我使用 4/3的比例, 也有16/9的比例,即1.7左右。
同时应该注意到有两行代码
parameters.setRotation(90);
camera.setDisplayOrientation(90);
这两行代码是设置显示图像和获取的图片的偏转角度, 在竖屏格式下, 设置90偏转才能得到理想的照片。 否则你会发现预览和获取的图片与想要得到的图片偏转了90度。
另一个需要注意的是 previewSize和pictureSize 的width和height 和屏幕的width与height正好相反, 前两者width>height而后者width<height。 在调整surfaceView的宽度和高度时需要注意这个问题,可以使用下面代码调整:
/**分辨率定位4/3, 根据屏幕自身来进行匹配
* */
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
ViewGroup.LayoutParams params = mSurfaceView.getLayoutParams();
params.width = metrics.widthPixels;
params.height = (int) (metrics.widthPixels/3.0*4);
mSurfaceView.setLayoutParams(params);
ocrView.setLayoutParams(params);
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {//保存为jpeg格式的图片
TakePicEvent takePicEvent = new TakePicEvent();
Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
float width = bitmap.getWidth()*0.8f;
float height = width*0.75f;
int posx = (int) ((bitmap.getWidth()-width)/2.0);
int posy = (int)((bitmap.getHeight() - height)/2.0);
Rect r = new Rect(posx, posy, (int)(posx+width), (int)(posy+height));
Bitmap nb = Bitmap.createBitmap(bitmap, r.left,r.top, r.width(),r.height());
File file = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() +"img.jpg");
try {
FileOutputStream outputStream = new FileOutputStream(file);
nb.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
outputStream.close();
takePicEvent.setStatus(BaseEvent.EVENT_STATUS.EVENT_OK);
takePicEvent.setMsg(file.getAbsolutePath());
EventBus.getDefault().post(takePicEvent);
return;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//File file = new File(getBaseContext().getFilesDir(),String.format("%dimg.jpg",System.currentTimeMillis()));
takePicEvent.setStatus(BaseEvent.EVENT_STATUS.EVENT_ERROR);
takePicEvent.setMsg("获取图片失败,请确认存储卡可用");
EventBus.getDefault().post(takePicEvent);
}
});
附属:
有的系统带有权限管理,所以需要判断设备是否可以正常打开,即使你在配置文件中已经声明使用相机。 通常需要进行异常捕获和空值检测。
截取框显示窗口
public class OcrPreview extends View {
public OcrPreview(Context context) {
super(context);
}
public OcrPreview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OcrPreview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**蒙版设置*/
float width = getWidth()*0.8f;
float height = width*0.75f;
int posx = (int) ((getWidth()-width)/2.0);
int posy = (int)((getHeight() - height)/2.0);
Rect r = new Rect(posx, posy, (int)(posx+width), (int)(posy+height));
Paint paint = new Paint();
paint.setColor(Color.parseColor("#00ff00"));
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(r,paint);
}
}