Android实现身份证拍摄框
http://blog.csdn.net/lhbtm/article/details/55505668
最近项目需要实现身份证拍照的功能,系统的相机并不能满足需求,故需要自定义相机,先上效果图,使用了Camera+ SurfaceView;
布局文件
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.administrator.camerasurfaceview.CameraSurfaceView android:id="@+id/cameraSurfaceView" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.example.administrator.camerasurfaceview.CameraTopRectView android:id="@+id/rectOnCamera" android:layout_width="match_parent" android:layout_height="match_parent" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="20px" android:id="@+id/takePic" android:layout_width="80dp" android:layout_height="50dp" android:background="#88427ac7" android:text="拍照" android:textColor="#aaa" /> </RelativeLayout> </FrameLayout>
MainActivity
package com.example.administrator.camerasurfaceview; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { private Button button; private CameraSurfaceView mCameraSurfaceView; private RectOnCamera rectOnCamera; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // requestWindowFeature(Window.FEATURE_NO_TITLE); // // 全屏显示 // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_main); mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.cameraSurfaceView); button = (Button) findViewById(R.id.takePic); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mCameraSurfaceView.takePicture(); } }); } // public void autoFocus() { // mCameraSurfaceView.setAutoFocus(); // } }
自定义相机类
package com.example.administrator.camerasurfaceview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.hardware.Camera; import android.os.Environment; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.Toast; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; /** * Created by Administrator on 2017/2/15 0015.//自定义相机 */ public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback { private static final String TAG = "CameraSurfaceView"; private Context mContext; private SurfaceHolder holder; private Camera mCamera; private int mScreenWidth; private int mScreenHeight; private CameraTopRectView topView; public CameraSurfaceView(Context context) { this(context, null); } public CameraSurfaceView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; getScreenMetrix(context); topView = new CameraTopRectView(context,attrs); initView(); } //拿到手机屏幕大小 private void getScreenMetrix(Context context) { WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); WM.getDefaultDisplay().getMetrics(outMetrics); mScreenWidth = outMetrics.widthPixels; mScreenHeight = outMetrics.heightPixels; } private void initView() { holder = getHolder();//获得surfaceHolder引用 holder.addCallback(this); // holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置类型 } @Override public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "surfaceCreated"); if (mCamera == null) { mCamera = Camera.open();//开启相机 try { mCamera.setPreviewDisplay(holder);//摄像头画面显示在Surface上 } catch (IOException e) { e.printStackTrace(); } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.i(TAG, "surfaceChanged"); setCameraParams(mCamera, mScreenWidth, mScreenHeight); mCamera.startPreview(); // mCamera.takePicture(null, null, jpeg); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i(TAG, "surfaceDestroyed"); mCamera.stopPreview();//停止预览 mCamera.release();//释放相机资源 mCamera = null; holder = null; } @Override public void onAutoFocus(boolean success, Camera Camera) { if (success) { Log.i(TAG, "onAutoFocus success="+success); System.out.println(success); } } private void setCameraParams(Camera camera, int width, int height) { Log.i(TAG,"setCameraParams width="+width+" height="+height); Camera.Parameters parameters = mCamera.getParameters(); // 获取摄像头支持的PictureSize列表 List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes(); for (Camera.Size size : pictureSizeList) { Log.i(TAG, "pictureSizeList size.width=" + size.width + " size.height=" + size.height); } /**从列表中选取合适的分辨率*/ Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width)); if (null == picSize) { Log.i(TAG, "null == picSize"); picSize = parameters.getPictureSize(); } Log.i(TAG, "picSize.width=" + picSize.width + " picSize.height=" + picSize.height); // 根据选出的PictureSize重新设置SurfaceView大小 float w = picSize.width; float h = picSize.height; parameters.setPictureSize(picSize.width,picSize.height); this.setLayoutParams(new FrameLayout.LayoutParams((int) (height*(h/w)), height)); // 获取摄像头支持的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.Size preSize = getProperSize(previewSizeList, ((float) height) / width); if (null != preSize) { Log.i(TAG, "preSize.width=" + preSize.width + " preSize.height=" + preSize.height); parameters.setPreviewSize(preSize.width, preSize.height); } parameters.setJpegQuality(100); // 设置照片质量 if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式 } mCamera.cancelAutoFocus();//自动对焦。 mCamera.setDisplayOrientation(90);// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示 mCamera.setParameters(parameters); } /** * 从列表中选取合适的分辨率 * 默认w:h = 4:3 * <p>注意:这里的w对应屏幕的height * h对应屏幕的width<p/> */ private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) { Log.i(TAG, "screenRatio=" + screenRatio); Camera.Size result = null; for (Camera.Size size : pictureSizeList) { float currentRatio = ((float) size.width) / size.height; if (currentRatio - screenRatio == 0) { result = size; break; } } if (null == result) { for (Camera.Size size : pictureSizeList) { float curRatio = ((float) size.width) / size.height; if (curRatio == 4f / 3) {// 默认w:h = 4:3 result = size; break; } } } return result; } // 拍照瞬间调用 private Camera.ShutterCallback shutter = new Camera.ShutterCallback() { @Override public void onShutter() { Log.i(TAG,"shutter"); System.out.println("执行了吗+1"); } }; // 获得没有压缩过的图片数据 private Camera.PictureCallback raw = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera Camera) { Log.i(TAG, "raw"); System.out.println("执行了吗+2"); } }; //创建jpeg图片回调数据对象 private Camera.PictureCallback jpeg = new Camera.PictureCallback() { private Bitmap bitmap; @Override public void onPictureTaken(byte[] data, Camera Camera) { topView.draw(new Canvas()); BufferedOutputStream bos = null; Bitmap bm = null; if(data != null){ } try { // 获得图片 bm = BitmapFactory.decodeByteArray(data, 0, data.length); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String filePath = "/sdcard/dyk"+System.currentTimeMillis()+".JPEG";//照片保存路径 // //图片存储前旋转 Matrix m = new Matrix(); int height = bm.getHeight(); int width = bm.getWidth(); m.setRotate(90); //旋转后的图片 bitmap = Bitmap.createBitmap(bm, 0, 0, width, height, m, true); System.out.println("执行了吗+3"); File file = new File(filePath); if (!file.exists()){ file.createNewFile(); } bos = new BufferedOutputStream(new FileOutputStream(file)); // Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, // data.length); Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap, topView.getViewWidth(), topView.getViewHeight(), true); bm = Bitmap.createBitmap(sizeBitmap, topView.getRectLeft(), topView.getRectTop(), topView.getRectRight() - topView.getRectLeft(), topView.getRectBottom() - topView.getRectTop());// 截取 bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);//将图片压缩到流中 }else{ Toast.makeText(mContext,"没有检测到内存卡", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } finally { try { bos.flush();//输出 bos.close();//关闭 bm.recycle();// 回收bitmap空间 mCamera.stopPreview();// 关闭预览 mCamera.startPreview();// 开启预览 } catch (IOException e) { e.printStackTrace(); } } } }; public void takePicture(){ //设置参数,并拍照 setCameraParams(mCamera, mScreenWidth, mScreenHeight); // 当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览 mCamera.takePicture(null,null, jpeg); } // public void setAutoFocus(){ // mCamera.autoFocus(this); // } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
自定义身份证方框
package com.example.administrator.camerasurfaceview; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; import android.graphics.Paint.Style; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.WindowManager; //import cn.jiazhengye.panda_home.application.BaseApplication; //import cn.jiazhengye.panda_home.common.Constant; //import cn.jiazhengye.panda_home.utils.DisplayUtil; //import cn.jiazhengye.panda_home.utils.LoggerUtil; class CameraTopRectView extends View { private int panelWidth; private int panelHeght; private int viewWidth; private int viewHeight; public int rectWidth; public int rectHeght; private int rectTop; private int rectLeft; private int rectRight; private int rectBottom; private int lineLen; private int lineWidht; private static final int LINE_WIDTH = 5; private static final int TOP_BAR_HEIGHT = 50; private static final int BOTTOM_BTN_HEIGHT = 66; // private static final int TOP_BAR_HEIGHT = Constant.RECT_VIEW_TOP; // private static final int BOTTOM_BTN_HEIGHT = Constant.RECT_VIEW_BOTTOM; private static final int LEFT_PADDING = 10; private static final int RIGHT_PADDING = 10; private static final String TIPS = "请将身份证放入到方框中"; private Paint linePaint; private Paint wordPaint; private Rect rect; private int baseline; public CameraTopRectView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub Activity activity = (Activity) context; WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); panelWidth = wm.getDefaultDisplay().getWidth();//拿到屏幕的宽 panelHeght = wm.getDefaultDisplay().getHeight();//拿到屏幕的高 //高度不需要dp转换px,不然整体相机会向上移动一小节 // viewHeight = panelHeght - (int) DisplayUtil.dp2px(activity,TOP_BAR_HEIGHT + BOTTOM_BTN_HEIGHT); viewHeight = panelHeght; //viewHeight,界面的高,viewWidth,界面的宽 viewWidth = panelWidth; /*rectWidth = panelWidth - UnitUtils.getInstance(activity).dip2px( LEFT_PADDING + RIGHT_PADDING);*/ rectWidth = panelWidth - (int) DisplayUtil.dp2px(activity,LEFT_PADDING + RIGHT_PADDING); rectHeght = (int) (rectWidth * 54 / 85.6); // 相对于此view rectTop = (viewHeight - rectHeght) / 2; rectLeft = (viewWidth - rectWidth) / 2; rectBottom = rectTop + rectHeght; rectRight = rectLeft + rectWidth; lineLen = panelWidth / 8; linePaint = new Paint(); linePaint.setAntiAlias(true); linePaint.setColor(Color.rgb(0xdd, 0x42, 0x2f)); linePaint.setStyle(Style.STROKE); linePaint.setStrokeWidth(LINE_WIDTH);// 设置线宽 linePaint.setAlpha(255); wordPaint = new Paint(); wordPaint.setAntiAlias(true); wordPaint = new Paint(Paint.ANTI_ALIAS_FLAG); wordPaint.setStrokeWidth(3); wordPaint.setTextSize(35); rect = new Rect(rectLeft, rectTop - 80, rectRight, rectTop - 10); FontMetricsInt fontMetrics = wordPaint.getFontMetricsInt(); baseline = rect.top + (rect.bottom - rect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top; wordPaint.setTextAlign(Paint.Align.CENTER); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); wordPaint.setColor(Color.TRANSPARENT); canvas.drawRect(rect, wordPaint); //画蒙层 wordPaint.setColor(0xa0000000); rect = new Rect(0, viewHeight/2+rectHeght/2, viewWidth, viewHeight); canvas.drawRect(rect, wordPaint); rect = new Rect(0, 0, viewWidth, viewHeight/2-rectHeght/2); canvas.drawRect(rect, wordPaint); rect = new Rect(0, viewHeight/2-rectHeght/2, (viewWidth-rectWidth)/2, viewHeight/2+rectHeght/2); canvas.drawRect(rect, wordPaint); rect = new Rect(viewWidth-(viewWidth-rectWidth)/2, viewHeight/2-rectHeght/2, viewWidth, viewHeight/2+rectHeght/2); canvas.drawRect(rect, wordPaint); //重制rect 并画文字 吧文字置于rect中间 rect = new Rect(rectLeft, rectTop - 80, rectRight, rectTop - 10); wordPaint.setColor(Color.WHITE); canvas.drawText(TIPS, rect.centerX(), baseline, wordPaint); canvas.drawLine(rectLeft, rectTop, rectLeft + lineLen, rectTop, linePaint); canvas.drawLine(rectRight - lineLen, rectTop, rectRight, rectTop, linePaint); canvas.drawLine(rectLeft, rectTop, rectLeft, rectTop + lineLen, linePaint); canvas.drawLine(rectRight, rectTop, rectRight, rectTop + lineLen, linePaint); canvas.drawLine(rectLeft, rectBottom, rectLeft + lineLen, rectBottom, linePaint); canvas.drawLine(rectRight - lineLen, rectBottom, rectRight, rectBottom, linePaint); canvas.drawLine(rectLeft, rectBottom - lineLen, rectLeft, rectBottom, linePaint); canvas.drawLine(rectRight, rectBottom - lineLen, rectRight, rectBottom, linePaint); } public int getRectLeft() { return rectLeft; } public int getRectTop() { return rectTop; } public int getRectRight() { return rectRight; } public int getRectBottom() { return rectBottom; } public int getViewWidth() { return viewWidth; } public int getViewHeight() { return viewHeight; } }
DisplayUtil工具类
package com.example.administrator.camerasurfaceview; import android.content.Context; /** * Created by Administrator on 2017/2/16 0016. */ public class DisplayUtil { /** * 将px装换成dp,保证尺寸不变 * @param context * @param pxValue * @return */ public static int px2dp(Context context, float pxValue){ float density = context.getResources().getDisplayMetrics().density;//得到设备的密度 return (int) (pxValue/density+0.5f); } public static int dp2px(Context context, float dpValue){ float density = context.getResources().getDisplayMetrics().density; return (int) (dpValue*density+0.5f); } public static int px2sp(Context context, float pxValue){ float scaleDensity = context.getResources().getDisplayMetrics().scaledDensity;//缩放密度 return (int) (pxValue/scaleDensity+0.5f); } public static int sp2px(Context context, float spValue) { float scaleDensity = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue*scaleDensity+0.5f); } }