自定义Android照相机,实现只拍摄矩形区域(如矩形区域面积为0或与屏幕大小一致,则拍摄全屏照片)。完美解决预览及拍照时的照片拉伸失真,旋转等问题
预览效果:html
拍照效果:java
准备工做,开启权限android
<!-- 振动权限 --> <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做为遮罩层canvas
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方法ide
@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测试经过),如之后遇到问题再修改。ui
定义一个接口类OnCaptureCallback并定义onCapture方法。在生成照片后回调该方法,返回生成的文件路径this
public interface OnCaptureCallback { public void onCapture(boolean success, String filePath); }
在Activity中实现OnCaptureCallback接口而且重写onCapture方法.net
@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(); } });
说明:
一、代码中有的人会在执行this.camera.setParameters(p)这一行的时候JNI底层会报错,缘由就是由于设置的预览尺寸或照片尺寸或是其余参数跟设备支持的参数不匹配。
二、关于竖屏拍照的时候虽然已经设置了setDisplayOrientation参数,但拍出的照片仍是旋转了90度。网上给出的解释是无论横屏仍是竖屏拍出的照片都是按横屏来拍照的,因此在拍照获取到照片数据的时候须要本身手动将照片竖屏拍出的照片旋转回来。
三、在第一次发布项目后,用户反应照相、裁剪有问题(尺寸不对)。后来调试发如今裁剪照片的时候计算裁剪尺寸的算法有问题(对低版本、低分辨率、支持拍照照片的尺寸支持很差,甚至抛异常),不过把算法修改事后就行了。就是计算裁剪尺寸的时候要改为按照设置的取景框的尺寸和设置的照片尺寸按比例裁剪,这里就不贴代码了,本身研究下
思考:
闪光灯设为AUTO,本人三星S4测试第一次拍照按下快门的时候闪光灯会闪一下,之后拍照就不闪了;旋转下屏幕方向第一次拍照按下快门也会闪一次。平板一切正常。若有人知道怎么解决这个问题,望指教。
四、这个版本主要针对分辨率为16:9的尺寸来写的,要支持4:3的分辨率的话,须要修改获取预览及照片尺寸的算法。也是在项目上线后发现的不兼容问题,不过也已经修正了。很好改的
最后因为本人CSDN积分很少了,赚点积分,赚点辛苦费,望支持。谢谢
源代码下载地址: