自定义Android照相机,实现只拍摄矩形区域(如矩形区域面积为0或与屏幕大小一致,则拍摄全屏照片)。完美解决预览及拍照时的照片拉伸失真,旋转等问题
预览效果:
拍照效果:
准备工作,开启权限
<!-- 振动权限 -->
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- 照相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 屏幕旋转权限 -->
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
<uses-feature android:name="android.hardware.camera" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="true" />
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 开启闪光灯权限 -->
<uses-permission android:name="android.permission.FLASHLIGHT" />
自定义MaskSurfaceView组件(底层SurfaceView控件和自定义上层View控件叠加放在FrameLayout中)
public MaskSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
surfaceView = new MSurfaceView(context);
imageView = new MaskView(context);
this.addView(surfaceView,LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
this.addView(imageView,LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
//在构造方法中获取屏幕尺寸,以备后用
Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
screenHeight = display.getHeight();
screenWidth = display.getWidth();
//让CameraHelper拥有该控件对象
CameraHelper.getInstance().setMaskSurfaceView(this);
}
MaskSurfaceView内部类中自定义View作为遮罩层
public MaskView(Context context) {
super(context);
// 绘制中间透明区域矩形边界的Paint
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(Color.BLUE);
linePaint.setStyle(Style.STROKE);
linePaint.setStrokeWidth(3f);
linePaint.setAlpha(80);
//绘制四周矩形阴影区域
rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
rectPaint.setColor(Color.YELLOW);
rectPaint.setStyle(Style.FILL);
rectPaint.setAlpha(20);
}
重写自定义View遮罩层的onDraw方法
@Override
protected void onDraw(Canvas canvas) {
if(maskHeight==0 && maskWidth==0){
return;
}
if(maskHeight==height || maskWidth==width){
return;
}
if((height>width&&maskHeight<maskWidth) || (height<width&&maskHeight>maskWidth)){
int temp = maskHeight;
maskHeight = maskWidth;
maskWidth = temp;
}
int h = Math.abs((height-maskHeight)/2);
int w = Math.abs((width-maskWidth)/2);
// 上
canvas.drawRect(0, 0, width, h, this.rectPaint);
// 右
canvas.drawRect(width-w, h, width, height-h, this.rectPaint);
// 下
canvas.drawRect(0, height-h, width, height, this.rectPaint);
// 左
canvas.drawRect(0, h, w, h+maskHeight, this.rectPaint);
canvas.drawRect(w, h, w+maskWidth, h+maskHeight, this.linePaint);
super.onDraw(canvas);
}
定义CameraHelper类,分离业务和显示。将Camera对象的所有操作都定义在这里(代码有点多,待会贴上源代码。注释写的清清楚楚的,自己看)
在CameraHelper中initParameters方法设置照相机参数有些地方如设置相机分辨率,照片尺寸,旋转之类的值得注意。这部分代码是参考了网上的帖子和官方例子写出来,个人觉得目前是可以兼容所有照相机参数设置问题(三星S4,华为Pad,小米3测试通过),如以后遇到问题再修改。
定义一个接口类OnCaptureCallback并定义onCapture方法。在生成照片后回调该方法,返回生成的文件路径
public interface OnCaptureCallback {
public void onCapture(boolean success, String filePath);
}
在Activity中实现OnCaptureCallback接口并且重写onCapture方法
@Override
public void onCapture(boolean success, String filepath) {
this.filepath = filepath;
String message = "拍照成功";
if(!success){
message = "拍照失败";
CameraHelper.getInstance().startPreview();
this.imageView.setVisibility(View.GONE);
this.surfaceview.setVisibility(View.VISIBLE);
}else{
this.imageView.setVisibility(View.VISIBLE);
this.surfaceview.setVisibility(View.GONE);
this.imageView.setImageBitmap(BitmapFactory.decodeFile(filepath));
}
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
Activity中分别为按钮添加事件
<pre class="java" name="code">//拍照
btn_capture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
btn_capture.setEnabled(false);
btn_ok.setEnabled(true);
btn_recapture.setEnabled(true);
CameraHelper.getInstance().tackPicture(RectCameraActivity.this);
}
});
//重拍
btn_recapture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
btn_capture.setEnabled(true);
btn_ok.setEnabled(false);
btn_recapture.setEnabled(false);
imageView.setVisibility(View.GONE);
surfaceview.setVisibility(View.VISIBLE);
deleteFile();
CameraHelper.getInstance().startPreview();
}
});
//确认
btn_ok.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
}
});
//取消
btn_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
deleteFile();
RectCameraActivity.this.finish();
}
});
说明:
1、代码中有的人会在执行this.camera.setParameters(p)这一行的时候JNI底层会报错,原因就是因为设置的预览尺寸或照片尺寸或是其他参数跟设备支持的参数不匹配。
2、关于竖屏拍照的时候虽然已经设置了setDisplayOrientation参数,但拍出的照片还是旋转了90度。网上给出的解释是不管横屏还是竖屏拍出的照片都是按横屏来拍照的,所以在拍照获取到照片数据的时候需要自己手动将照片竖屏拍出的照片旋转回来。
3、在第一次发布项目后,用户反应照相、裁剪有问题(尺寸不对)。后来调试发现在裁剪照片的时候计算裁剪尺寸的算法有问题(对低版本、低分辨率、支持拍照照片的尺寸支持不好,甚至抛异常),不过把算法修改过后就好了。就是计算裁剪尺寸的时候要改成按照设置的取景框的尺寸和设置的照片尺寸按比例裁剪,这里就不贴代码了,自己研究下
思考:
闪光灯设为AUTO,本人三星S4测试第一次拍照按下快门的时候闪光灯会闪一下,以后拍照就不闪了;旋转下屏幕方向第一次拍照按下快门也会闪一次。平板一切正常。如有人知道怎么解决这个问题,望指教。
4、这个版本主要针对分辨率为16:9的尺寸来写的,要支持4:3的分辨率的话,需要修改获取预览及照片尺寸的算法。也是在项目上线后发现的不兼容问题,不过也已经修正了。很好改的